| @@ -0,0 +1,22 @@ | |||
| kind: pipeline | |||
| name: default | |||
| platform: | |||
| os: linux | |||
| arch: arm64 | |||
| steps: | |||
| - name: install | |||
| image: ruby:2.4.5 | |||
| commands: | |||
| - gem install bundler | |||
| - bundle -v | |||
| - bundle install --jobs=1 --retry=1 | |||
| - name: test | |||
| image: ruby:2.4.5 | |||
| volumes: | |||
| - name: bundle | |||
| path: /usr/local/bundle | |||
| commands: | |||
| - rake | |||
| @@ -1,8 +1,6 @@ | |||
| source 'https://gems.ruby-china.com' | |||
| git_source(:github) { |repo| "https://github.com/#{repo}.git" } | |||
| ruby '2.3.7' | |||
| gem 'rails', '~> 5.2.0' | |||
| gem 'mysql2', '>= 0.4.4', '< 0.6.0' | |||
| gem 'puma', '~> 3.11' | |||
| @@ -19,8 +17,6 @@ gem 'kaminari', '~> 1.1', '>= 1.1.1' | |||
| gem 'bootsnap', '>= 1.1.0', require: false | |||
| gem 'gitlab', path: 'lib/gitlab-cli' | |||
| gem 'chinese_pinyin' | |||
| gem 'rack-cors' | |||
| @@ -74,6 +70,7 @@ group :development do | |||
| gem 'listen', '>= 3.0.5', '< 3.2' | |||
| gem 'spring' | |||
| gem 'spring-watcher-listen', '~> 2.0.0' | |||
| gem "annotate", "~> 2.6.0" | |||
| end | |||
| group :test do | |||
| @@ -1,10 +1,3 @@ | |||
| PATH | |||
| remote: lib/gitlab-cli | |||
| specs: | |||
| gitlab (3.2.0) | |||
| httparty | |||
| terminal-table | |||
| GEM | |||
| remote: https://gems.ruby-china.com/ | |||
| specs: | |||
| @@ -61,6 +54,9 @@ GEM | |||
| public_suffix (>= 2.0.2, < 5.0) | |||
| ancestry (3.0.7) | |||
| activerecord (>= 3.2.0) | |||
| annotate (2.6.5) | |||
| activerecord (>= 2.3.0) | |||
| rake (>= 0.8.7) | |||
| archive-zip (0.12.0) | |||
| io-like (~> 0.3.0) | |||
| arel (9.0.0) | |||
| @@ -138,9 +134,6 @@ GEM | |||
| harmonious_dictionary (0.0.1) | |||
| hashie (3.6.0) | |||
| htmlentities (4.3.4) | |||
| httparty (0.18.0) | |||
| mime-types (~> 3.0) | |||
| multi_xml (>= 0.5.2) | |||
| i18n (1.8.2) | |||
| concurrent-ruby (~> 1.0) | |||
| io-like (0.3.1) | |||
| @@ -177,9 +170,6 @@ GEM | |||
| mimemagic (~> 0.3.2) | |||
| maruku (0.7.3) | |||
| method_source (0.9.2) | |||
| mime-types (3.3.1) | |||
| mime-types-data (~> 3.2015) | |||
| mime-types-data (3.2019.1009) | |||
| mimemagic (0.3.4) | |||
| mini_mime (1.0.2) | |||
| mini_portile2 (2.4.0) | |||
| @@ -402,8 +392,6 @@ GEM | |||
| actionpack (>= 4.0) | |||
| activesupport (>= 4.0) | |||
| sprockets (>= 3.0.0) | |||
| terminal-table (1.8.0) | |||
| unicode-display_width (~> 1.1, >= 1.1.1) | |||
| thor (1.0.1) | |||
| thread_safe (0.3.6) | |||
| tilt (2.0.10) | |||
| @@ -437,6 +425,7 @@ DEPENDENCIES | |||
| acts-as-taggable-on (~> 6.0) | |||
| acts_as_list | |||
| ancestry | |||
| annotate (~> 2.6.0) | |||
| awesome_print | |||
| axlsx (~> 3.0.0.pre) | |||
| axlsx_rails (~> 0.5.2) | |||
| @@ -452,7 +441,6 @@ DEPENDENCIES | |||
| enumerize | |||
| faraday (~> 0.15.4) | |||
| font-awesome-sass (= 4.7.0) | |||
| gitlab! | |||
| grape-entity (~> 0.7.1) | |||
| groupdate (~> 4.1.0) | |||
| harmonious_dictionary (~> 0.0.1) | |||
| @@ -502,8 +490,5 @@ DEPENDENCIES | |||
| web-console (>= 3.3.0) | |||
| wkhtmltopdf-binary | |||
| RUBY VERSION | |||
| ruby 2.3.7p456 | |||
| BUNDLED WITH | |||
| 2.1.4 | |||
| @@ -110,33 +110,33 @@ class AccountsController < ApplicationController | |||
| # params[:login] 邮箱或者手机号 | |||
| # params[:code] 验证码 | |||
| # code_type 1:注册手机验证码 8:邮箱注册验证码 | |||
| # 本地forge注册入口 | |||
| def register | |||
| begin | |||
| # 查询验证码是否正确;type只可能是1或者8 | |||
| type = phone_mail_type(params[:login].strip) | |||
| code = params[:code].strip | |||
| # code = params[:code].strip | |||
| if type == 1 | |||
| uid_logger("start register by phone: type is #{type}") | |||
| pre = 'p' | |||
| email = nil | |||
| phone = params[:login] | |||
| verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 1).last | |||
| # verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 1).last | |||
| # TODO: 暂时限定邮箱注册 | |||
| return normal_status(-1, '只支持邮箱注册') | |||
| else | |||
| uid_logger("start register by email: type is #{type}") | |||
| pre = 'm' | |||
| email = params[:login] | |||
| phone = nil | |||
| verifi_code = VerificationCode.where(email: email, code: code, code_type: 8).last | |||
| return normal_status(-1, "该邮箱已注册") if User.exists?(mail: params[:login]) | |||
| return normal_status(-1, "邮箱格式错误") unless params[:login] =~ CustomRegexp::EMAIL | |||
| # verifi_code = VerificationCode.where(email: email, code: code, code_type: 8).last | |||
| end | |||
| uid_logger("start register: verifi_code is #{verifi_code}, code is #{code}, time is #{Time.now.to_i - verifi_code.try(:created_at).to_i}") | |||
| # uid_logger("start register: verifi_code is #{verifi_code}, code is #{code}, time is #{Time.now.to_i - verifi_code.try(:created_at).to_i}") | |||
| # check_code = (verifi_code.try(:code) == code.strip && (Time.now.to_i - verifi_code.created_at.to_i) <= 10*60) | |||
| # todo 上线前请删除万能验证码"513231" | |||
| unless code == "513231" && request.subdomain == "test-newweb" | |||
| return normal_status(-2, "验证码不正确") if verifi_code.try(:code) != code.strip | |||
| return normal_status(-2, "验证码已失效") if !verifi_code&.effective? | |||
| end | |||
| return normal_status(-1, "8~16位密码,支持字母数字和符号") unless params[:password] =~ CustomRegexp::PASSWORD | |||
| code = generate_identifier User, 8, pre | |||
| @@ -146,23 +146,20 @@ class AccountsController < ApplicationController | |||
| # 现在因为是验证码,所以在注册的时候就可以激活 | |||
| @user.activate | |||
| # 必须要用save操作,密码的保存是在users中 | |||
| if @user.save! | |||
| # todo user_extension | |||
| UserExtension.create!(user_id: @user.id) | |||
| # 注册完成,手机号或邮箱想可以奖励500金币 | |||
| # RewardGradeService.call( | |||
| # @user, | |||
| # container_id: @user.id, | |||
| # container_type: pre == 'p' ? 'Phone' : 'Mail', | |||
| # score: 500 | |||
| # ) | |||
| # 注册时,记录是否是引流用户 | |||
| ip = request.remote_ip | |||
| ua = UserAgent.find_by_ip(ip) | |||
| ua.update_column(:agent_type, UserAgent::USER_REGISTER) if ua | |||
| successful_authentication(@user) | |||
| # session[:user_id] = @user.id | |||
| normal_status("注册成功") | |||
| interactor = Gitea::RegisterInteractor.call({username: login, email: email, password: params[:password]}) | |||
| if interactor.success? | |||
| gitea_user = interactor.result | |||
| result = Gitea::User::GenerateTokenService.new(login, params[:password]).call | |||
| @user.gitea_token = result['sha1'] | |||
| @user.gitea_uid = gitea_user['id'] | |||
| if @user.save! | |||
| UserExtension.create!(user_id: @user.id) | |||
| successful_authentication(@user) | |||
| normal_status("注册成功") | |||
| end | |||
| else | |||
| tip_exception(-1, interactor.error) | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| @@ -6,6 +6,12 @@ class Admins::LaboratoriesController < Admins::BaseController | |||
| @laboratories = paginate laboratories.preload(:laboratory_users) | |||
| end | |||
| def new | |||
| respond_to do |format| | |||
| format.js | |||
| end | |||
| end | |||
| def create | |||
| Admins::CreateLaboratoryService.call(create_params) | |||
| render_ok | |||
| @@ -1,4 +1,11 @@ | |||
| class Admins::LaboratorySettingsController < Admins::BaseController | |||
| def new | |||
| respond_to do |format| | |||
| format.js | |||
| end | |||
| end | |||
| def show | |||
| @laboratory = current_laboratory | |||
| end | |||
| @@ -10,7 +10,7 @@ class ApplicationController < ActionController::Base | |||
| include LoggerHelper | |||
| include LoginHelper | |||
| include RegisterHelper | |||
| protect_from_forgery prepend: true, unless: -> { request.format.json? } | |||
| before_action :check_sign | |||
| @@ -343,7 +343,8 @@ class ApplicationController < ActionController::Base | |||
| elsif params[:debug] == 'student' | |||
| User.current = User.find 8686 | |||
| elsif params[:debug] == 'admin' | |||
| user = User.find 1 | |||
| logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....." | |||
| user = User.find 36480 | |||
| User.current = user | |||
| cookies.signed[:user_id] = user.id | |||
| end | |||
| @@ -384,11 +385,7 @@ class ApplicationController < ActionController::Base | |||
| end | |||
| def current_user | |||
| if Rails.env.development? | |||
| User.current = User.find 1 | |||
| else | |||
| User.current | |||
| end | |||
| User.current | |||
| end | |||
| ## 默认输出json | |||
| @@ -744,12 +741,12 @@ class ApplicationController < ActionController::Base | |||
| namespace = params[:owner] | |||
| id = params[:repo] || params[:id] | |||
| @project = Project.find_with_namespace(namespace, id) | |||
| @project, @owner = Project.find_with_namespace(namespace, id) | |||
| if @project and current_user.can_read_project?(@project) | |||
| logger.info "###########: has project and can read project" | |||
| @project | |||
| elsif current_user.is_a?(AnonymousUser) | |||
| elsif @project && current_user.is_a?(AnonymousUser) | |||
| logger.info "###########:This is AnonymousUser" | |||
| @project = nil if !@project.is_public? | |||
| render_forbidden and return | |||
| @@ -762,7 +759,7 @@ class ApplicationController < ActionController::Base | |||
| end | |||
| def load_repository | |||
| @repository ||= load_project.repository | |||
| @repository ||= load_project&.repository | |||
| end | |||
| private | |||
| @@ -2,7 +2,7 @@ | |||
| # | |||
| # 文件上传 | |||
| class AttachmentsController < ApplicationController | |||
| before_action :require_login, :check_auth, except: [:show] | |||
| before_action :require_login, :check_auth, except: [:show, :preview_attachment, :get_file] | |||
| before_action :find_file, only: %i[show destroy] | |||
| before_action :attachment_candown, only: [:show] | |||
| skip_before_action :check_sign, only: [:show, :create] | |||
| @@ -28,6 +28,15 @@ class AttachmentsController < ApplicationController | |||
| update_downloads(@file) | |||
| end | |||
| def get_file | |||
| normal_status(-1, "参数缺失") if params[:download_url].blank? | |||
| url = URI.encode(params[:download_url].to_s.gsub("http:", "https:")) | |||
| response = Faraday.get(url) | |||
| filename = params[:download_url].to_s.split("/").pop() | |||
| send_data(response.body.force_encoding("UTF-8"), filename: filename, type: "application/octet-stream", disposition: 'attachment') | |||
| end | |||
| def create | |||
| # 1. 本地存储 | |||
| # 2. 上传到云 | |||
| @@ -107,6 +116,26 @@ class AttachmentsController < ApplicationController | |||
| end | |||
| end | |||
| # 附件为视频时,点击播放 | |||
| def preview_attachment | |||
| attachment = Attachment.find_by(id: params[:id]) | |||
| dir_path = "#{Rails.root}/public/preview" | |||
| Dir.mkdir(dir_path) unless Dir.exist?(dir_path) | |||
| if params[:status] == "preview" | |||
| if system("cp -r #{absolute_path(local_path(attachment))} #{dir_path}/") | |||
| render json: {status: 1, url: "/preview/#{attachment.disk_filename}"} | |||
| else | |||
| normal_status(-1, "出现错误,请稍后重试") | |||
| end | |||
| else | |||
| if system("rm -rf #{dir_path}/#{attachment.disk_filename}") | |||
| normal_status(1, "操作成功") | |||
| else | |||
| normal_status(-1, "出现错误,请稍后重试") | |||
| end | |||
| end | |||
| end | |||
| private | |||
| def find_file | |||
| @file = | |||
| @@ -0,0 +1,48 @@ | |||
| class Ci::BaseController < ApplicationController | |||
| include Ci::DbConnectable | |||
| before_action :require_login | |||
| before_action :connect_to_ci_database, if: -> { current_user && !current_user.is_a?(AnonymousUser) && !current_user.devops_uninit? } | |||
| before_action :connect_to_ci_database, only: :load_repo | |||
| def load_repo | |||
| namespace = params[:owner] | |||
| id = params[:repo] || params[:id] | |||
| @ci_user, @repo = Ci::Repo.find_with_namespace(namespace, id) | |||
| end | |||
| private | |||
| def authorize_access_project! | |||
| unless @project.manager?(current_user) | |||
| return render_forbidden | |||
| end | |||
| end | |||
| def authenticate_manager! | |||
| unless @project.manager?(current_user) | |||
| return render_forbidden | |||
| end | |||
| end | |||
| def authenticate_admin! | |||
| return render_forbidden unless current_user.admin? | |||
| end | |||
| def authorize_owner! | |||
| unless @project.owner?(current_user) | |||
| return render_forbidden | |||
| end | |||
| end | |||
| def find_cloud_account | |||
| @cloud_account ||= current_user.ci_cloud_account | |||
| @cloud_account.blank? ? nil : @cloud_account | |||
| end | |||
| def load_ci_user | |||
| @ci_user ||= Ci::User.find_by(user_login: params[:owner]) | |||
| @ci_user.blank? ? raise("未找到相关的记录") : @ci_user | |||
| end | |||
| end | |||
| @@ -0,0 +1,54 @@ | |||
| class Ci::BuildsController < Ci::BaseController | |||
| include RepositoriesHelper | |||
| before_action :load_project | |||
| before_action :authorize_owner!, only: [:restart, :stop] | |||
| before_action :load_repo | |||
| before_action :find_cloud_account, except: [:index, :show] | |||
| def index | |||
| @user = current_user | |||
| scope = @repo.builds | |||
| scope = Ci::Builds::ListQuery.call(@repo, params) | |||
| @total_count = scope.map(&:build_id).size | |||
| @builds = paginate scope | |||
| end | |||
| def show | |||
| @build = @repo.builds.includes(stages: [:steps]).find_by(build_number: params[:build]) | |||
| end | |||
| def restart | |||
| result = Ci::Drone::API.new(@ci_user.user_hash, @cloud_account.drone_url, @repo.repo_namespace, @repo.repo_name, number: params[:build]).restart | |||
| render json: result | |||
| end | |||
| def stop | |||
| result = Ci::Drone::API.new(@ci_user.user_hash, @cloud_account.drone_url, @repo.repo_namespace, @repo.repo_name, number: params[:build]).stop | |||
| render json: result | |||
| end | |||
| def logs | |||
| # TODO **待优化** | |||
| # 因直接操作ci库,如下查询待优化,可直接根据log id查询即可 | |||
| build = @repo.builds.find_by(build_number: params[:build]) | |||
| return render_not_found("Couldn't found build with 'number'= #{params[:build]}") if build.blank? | |||
| stage = build.stages.includes(steps: [:log]).find_by(stage_number: params[:stage]) | |||
| return render_not_found("Couldn't found build with 'number'= #{params[:stage]}") if stage.blank? | |||
| step = stage.steps.find_by(step_number: params[:step]) | |||
| return render_not_found("Couldn't found build with 'number'= #{params[:step]}") if step.blank? | |||
| log = step.log | |||
| result = log.blank? ? nil : (log.log_data[0..5].include?('null') ? nil : JSON.parse(log.log_data)) | |||
| # result = Ci::Drone::API.new(@user.user_hash, @cloud_account.drone_url, @repo.repo_namespace, @repo.repo_name, build: params[:build], stage: params[:stage], step: params[:step]).logs | |||
| render json: result | |||
| end | |||
| end | |||
| @@ -0,0 +1,101 @@ | |||
| class Ci::CloudAccountsController < Ci::BaseController | |||
| include Ci::CloudAccountManageable | |||
| skip_before_action :connect_to_ci_database, only: %i[create bind] | |||
| before_action :load_project, only: %i[create activate] | |||
| before_action :authorize_owner!, only: %i[create activate] | |||
| before_action :load_repo, only: %i[activate] | |||
| before_action :find_cloud_account, only: %i[show oauth_grant] | |||
| before_action :validate_params!, only: %i[create bind] | |||
| before_action only: %i[create bind] do | |||
| connect_to_ci_database(master_db: true) | |||
| end | |||
| def create | |||
| flag, msg = check_bind_cloud_account! | |||
| return render_error(msg) if flag === true | |||
| ActiveRecord::Base.transaction do | |||
| @cloud_account = bind_account! | |||
| if @cloud_account.blank? | |||
| render_error('激活失败, 请检查你的云服务器信息是否正确.') | |||
| raise ActiveRecord::Rollback | |||
| else | |||
| current_user.set_drone_step!(User::DEVOPS_UNVERIFIED) | |||
| render_ok(redirect_url: @cloud_account.authenticate_url) | |||
| end | |||
| end | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| def activate | |||
| return render_error('请先认证') unless current_user.ci_certification? | |||
| begin | |||
| @cloud_account = Ci::CloudAccount.find params[:id] | |||
| ActiveRecord::Base.transaction do | |||
| if @repo | |||
| return render_error('该项目已经激活') if @repo.repo_active? | |||
| @repo.activate!(@ci_user.user_id) | |||
| else | |||
| @repo = Ci::Repo.auto_create!(@ci_user, @project) | |||
| @user.update_column(:user_syncing, false) | |||
| end | |||
| result = bind_hook!(current_user, @cloud_account, @repo) | |||
| @project.update_columns(open_devops: true, gitea_webhook_id: result['id']) | |||
| @cloud_account.update_column(:ci_user_id, @ci_user.user_id) | |||
| end | |||
| render_ok | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| end | |||
| def show | |||
| end | |||
| def bind | |||
| flag, msg = check_bind_cloud_account! | |||
| return render_error(msg) if flag === true | |||
| ActiveRecord::Base.transaction do | |||
| @cloud_account = bind_account! | |||
| if @cloud_account.blank? | |||
| render_error('激活失败, 请检查你的云服务器信息是否正确.') | |||
| raise ActiveRecord::Rollback | |||
| else | |||
| current_user.set_drone_step!(User::DEVOPS_UNVERIFIED) | |||
| end | |||
| end | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| def unbind | |||
| ActiveRecord::Base.transaction do | |||
| unbind_account! | |||
| render_ok | |||
| end | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| def oauth_grant | |||
| password = params[:password].to_s | |||
| return render_error('你输入的密码不正确.') unless current_user.check_password?(password) | |||
| oauth = current_user.oauths.last | |||
| return render_error("服务器出小差了.") if oauth.blank? | |||
| result = gitea_oauth_grant!(password, oauth) | |||
| return render_error('授权失败.') unless result === true | |||
| current_user.set_drone_step!(User::DEVOPS_CERTIFICATION) | |||
| end | |||
| private | |||
| def validate_params! | |||
| Ci::CreateCloudAccountForm.new(devops_params).validate! | |||
| end | |||
| end | |||
| @@ -0,0 +1,20 @@ | |||
| class Ci::LanguagesController < Ci::BaseController | |||
| # TODO 需要开启权限认证,只有该项目devops初始化成功后才能获取语言列表 | |||
| before_action :find_langugae, only: :show | |||
| def index | |||
| @languages = Ci::Language.by_usage_amount_desc | |||
| end | |||
| def show | |||
| end | |||
| def common | |||
| @languages = Ci::Language.six_common | |||
| end | |||
| private | |||
| def find_langugae | |||
| @language = Ci::Language.find params[:id] | |||
| end | |||
| end | |||
| @@ -0,0 +1,73 @@ | |||
| class Ci::ProjectsController < Ci::BaseController | |||
| include RepositoriesHelper | |||
| include Ci::CloudAccountManageable | |||
| before_action :load_project | |||
| before_action :load_repo, only: [:update_trustie_pipeline, :activate, :deactivate] | |||
| before_action :authorize_owner!, only: [:authorize] | |||
| before_action :find_cloud_account, only: [:authorize, :activate, :deactivate] | |||
| def authorize | |||
| @user = current_user | |||
| end | |||
| # get .trustie-pipeline.yml file | |||
| def get_trustie_pipeline | |||
| file_path_uri = URI.parse('.trustie-pipeline.yml') | |||
| interactor = Repositories::EntriesInteractor.call(@project.owner, @project.identifier, file_path_uri, ref: params[:ref] || "master") | |||
| if interactor.success? | |||
| file = interactor.result | |||
| return render json: {} if file[:status] | |||
| json = {name: file['name'], path: file['path'], sha: file['sha'], content: render_decode64_content(file['content'])} | |||
| render json: json | |||
| end | |||
| end | |||
| def update_trustie_pipeline | |||
| interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, params[:owner], params.merge(identifier: @project.identifier)) | |||
| if interactor.success? | |||
| @file = interactor.result | |||
| render_result(1, "更新成功") | |||
| else | |||
| render_error(interactor.error) | |||
| end | |||
| end | |||
| def activate | |||
| return render_error('你还未认证') unless current_user.ci_certification? | |||
| begin | |||
| ActiveRecord::Base.transaction do | |||
| if @repo | |||
| return render_error('该项目已经激活') if @repo.repo_active? | |||
| if @project.ci_reactivate? | |||
| @project.ci_reactivate!(@repo) | |||
| return render_ok | |||
| end | |||
| @repo.activate!(@ci_user.user_id) | |||
| else | |||
| @repo = Ci::Repo.auto_create!(@ci_user, @project) | |||
| @ci_user.update_column(:user_syncing, false) | |||
| end | |||
| result = bind_hook!(current_user, @cloud_account, @repo) | |||
| @project.update_columns(open_devops: true, gitea_webhook_id: result['id']) | |||
| @project.increment!(:open_devops_count) | |||
| @cloud_account.update_column(:ci_user_id, @ci_user.user_id) | |||
| end | |||
| render_ok | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| end | |||
| def deactivate | |||
| return render_error('该项目已经取消激活') if !@repo.repo_active? | |||
| @project.update_column(:open_devops, false) | |||
| @repo.deactivate! | |||
| render_ok | |||
| end | |||
| end | |||
| @@ -0,0 +1,15 @@ | |||
| class CompareController < ApplicationController | |||
| # skip_before_action :require_login | |||
| before_action :load_repository | |||
| def index | |||
| end | |||
| def show | |||
| base_ref = Addressable::URI.unescape(params[:base]) | |||
| @ref = head_ref = Addressable::URI.unescape(params[:head]&.split('.json')[0]) | |||
| @compare_result = Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, base_ref, head_ref) | |||
| # render json: @compare_result | |||
| end | |||
| end | |||
| @@ -0,0 +1,138 @@ | |||
| module Ci::CloudAccountManageable | |||
| extend ActiveSupport::Concern | |||
| included do | |||
| end | |||
| def bind_account! | |||
| # 1. 保存华为云服务器帐号 | |||
| create_params = devops_params.merge(ip_num: IPAddr.new(devops_params[:ip_num].strip).to_i, secret: Ci::CloudAccount.encrypted_secret(devops_params[:secret])) | |||
| cloud_account = Ci::CloudAccount.new(create_params) | |||
| cloud_account.user = current_user | |||
| cloud_account.save! | |||
| # 2. 生成oauth2应用程序的client_id和client_secrete | |||
| gitea_oauth = Gitea::Oauth2::CreateService.call(current_user.gitea_token, {name: "pipeline-#{SecureRandom.hex(8)}", redirect_uris: ["#{cloud_account.drone_url}/login"]}) | |||
| logger.info "######### gitea_oauth: #{gitea_oauth}" | |||
| oauth = Oauth.new(client_id: gitea_oauth['client_id'], | |||
| client_secret: gitea_oauth['client_secret'], | |||
| redirect_uri: gitea_oauth['redirect_uris'], | |||
| gitea_oauth_id: gitea_oauth['id'], | |||
| user_id: current_user.id) | |||
| oauth.save! | |||
| # 创建数据ci端数据库 | |||
| database_result = auto_create_database!(@connection, "#{current_user.login}_drone") | |||
| logger.info "[CI::DbConnectable] auto_create_database's result: #{database_result}" | |||
| # 初始化表结构 | |||
| sub_connection = connect_to_ci_database | |||
| auto_create_table_structure!(sub_connection) | |||
| rpc_secret = SecureRandom.hex 16 | |||
| logger.info "######### rpc_secret: #{rpc_secret}" | |||
| # 3. 创建drone server | |||
| drone_server_cmd = Ci::Drone::Server.new(current_user.login, oauth.client_id, oauth.client_secret, cloud_account.drone_host, rpc_secret).generate_cmd | |||
| logger.info "######### drone_server_cmd: #{drone_server_cmd}" | |||
| # 4. 创建drone client | |||
| drone_client_cmd = Ci::Drone::Client.new(oauth.client_id, cloud_account.drone_ip, rpc_secret).generate_cmd | |||
| logger.info "######### drone_client_cmd: #{drone_client_cmd}" | |||
| # 5. 登录远程服务器,启动drone服务 | |||
| result = Ci::Drone::Start.new(cloud_account.account, cloud_account.visible_secret, cloud_account.drone_ip, drone_server_cmd, drone_client_cmd).run | |||
| logger.info "######### result: #{result}" | |||
| redirect_url = "#{cloud_account.drone_url}/login" | |||
| logger.info "######### redirect_url: #{redirect_url}" | |||
| return nil unless result.present? | |||
| result && !result.blank? ? cloud_account : nil | |||
| end | |||
| def unbind_account! | |||
| cloud_account = current_user.ci_cloud_account | |||
| return render_error('你未绑定CI服务器') if current_user.devops_step == User::DEVOPS_UNINIT || cloud_account.blank? | |||
| cloud_account.destroy! unless cloud_account.blank? | |||
| @connection.execute("DROP DATABASE IF EXISTS #{current_user.login}_drone") # TOTO drop drone database | |||
| current_user.unbind_account! | |||
| end | |||
| def bind_hook!(user, cloud_account, repo) | |||
| hook_params = { | |||
| active: true, | |||
| config: { | |||
| content_type: "json", | |||
| url: cloud_account.drone_url + "/hook?secret=#{repo.repo_signer}" | |||
| }, | |||
| type: "gitea" | |||
| } | |||
| result = Gitea::Hooks::CreateService.call(user.gitea_token, user.login, repo.repo_name, hook_params) | |||
| result[:status].present? ? nil : result | |||
| end | |||
| def check_bind_cloud_account! | |||
| return [true, "你已经绑定了云帐号."] unless current_user.ci_cloud_account.blank? | |||
| ip_num = IPAddr.new(devops_params[:ip_num]).to_i | |||
| Ci::CloudAccount.exists?(ip_num: ip_num) ? [true, "#{devops_params[:ip_num]}服务器已被使用."] : [false, nil] | |||
| end | |||
| def gitea_auto_create_auth_grant!(gitea_oauth_id) | |||
| connection = Gitea::Database.set_connection.connection | |||
| unix_time = Time.now.to_i | |||
| # 目前直接操作db,可以建立对应的model进行操作 | |||
| sql = "INSERT INTO oauth2_grant ( user_id, application_id, counter, created_unix, updated_unix ) VALUES ( #{current_user.gitea_uid}, #{gitea_oauth_id}, 0, #{unix_time}, #{unix_time} );" | |||
| connection.execute(sql) | |||
| end | |||
| def gitea_oauth_grant!(password, oauth) | |||
| gitea_auto_create_auth_grant!(oauth&.gitea_oauth_id) | |||
| state = SecureRandom.hex(8) | |||
| # redirect_uri eg: | |||
| # https://localhost:3000/login/oauth/authorize?client_id=94976481-ad0e-4ed4-9247-7eef106007a2&redirect_uri=http%3A%2F%2F121.69.81.11%3A80%2Flogin&response_type=code&state=9cab990b9cfb1805 | |||
| redirect_uri = CGI.escape("#{@cloud_account.drone_url}/login") | |||
| grant_url = "#{Gitea.gitea_config[:domain]}/login/oauth/authorize?client_id=#{oauth&.client_id}&redirect_uri=#{redirect_uri}&response_type=code&state=#{state}" | |||
| logger.info "[gitea] grant_url: #{grant_url}" | |||
| conn = Faraday.new(url: grant_url) do |req| | |||
| req.request :url_encoded | |||
| req.adapter Faraday.default_adapter | |||
| req.basic_auth(current_user.login, password) | |||
| end | |||
| response = conn.get | |||
| logger.info "[gitea] response headers: #{response.headers}" | |||
| drone_oauth_user!(response.headers.to_h['location'], state) | |||
| end | |||
| def drone_oauth_user!(url, state) | |||
| logger.info "[drone] drone_oauth_user url: #{url}" | |||
| conn = Faraday.new(url: url) do |req| | |||
| req.request :url_encoded | |||
| req.adapter Faraday.default_adapter | |||
| req.headers["cookie"] = "_session_=#{SecureRandom.hex(28)}; _oauth_state_=#{state}" | |||
| end | |||
| response = conn.get | |||
| logger.info "[drone] response headers: #{response.headers}" | |||
| response.headers['location'].include?('error') ? false : true | |||
| end | |||
| private | |||
| def devops_params | |||
| params.permit(:account, :secret, :ip_num) | |||
| end | |||
| end | |||
| @@ -0,0 +1,40 @@ | |||
| module Ci::DbConnectable | |||
| extend ActiveSupport::Concern | |||
| included do | |||
| end | |||
| # Dynamically sets the database connection. | |||
| def connect_to_ci_database(options={}) | |||
| master_db = options[:master_db] || false | |||
| config = Rails.application.config_for(:configuration).symbolize_keys! | |||
| db_config = config[:ci_db_server].symbolize_keys! | |||
| raise 'ci database config missing' if db_config.blank? | |||
| req_params = { | |||
| host: db_config[:host], | |||
| username: db_config[:username], | |||
| password: db_config[:password], | |||
| port: db_config[:port] | |||
| } | |||
| req_params = req_params.merge(database: "#{current_user.login}_#{db_config[:database]}") unless master_db === true | |||
| db_params = Ci::Database.get_connection_params(req_params) | |||
| @connection = Ci::Database.set_connection(db_params).connection | |||
| end | |||
| def auto_create_database!(connection, database) | |||
| Rails.logger.info "[CI::DbConnectable] auto_create_database's connection: #{connection}" | |||
| connection.execute("CREATE DATABASE IF NOT EXISTS #{database}") | |||
| end | |||
| def auto_create_table_structure!(connection) | |||
| Rails.logger.info "[CI::DbConnectable] auto_create_table_structure's connection: #{connection}" | |||
| sqls = Ci::Schema.statement.split(';').map(&:strip).reject { |e| e.to_s.empty? } | |||
| sqls.each do |sql| | |||
| con_result = connection.execute(sql) | |||
| Rails.logger.info "=============> ci create tabels result: #{con_result}" | |||
| end | |||
| end | |||
| end | |||
| @@ -17,7 +17,7 @@ module LoginHelper | |||
| :expires => 1.month.from_now, | |||
| :path => '/', | |||
| :secure => false, | |||
| :httponly => false | |||
| :httponly => true | |||
| } | |||
| if edu_setting('cookie_domain').present? | |||
| cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain')) | |||
| @@ -51,11 +51,16 @@ module LoginHelper | |||
| Rails.logger.info("####################__User.current_id______######{current_user.try(:id)}###___#{current_user&.logged?}") | |||
| if User.current.logged? | |||
| if autologin = cookies.delete(autologin_cookie_name) | |||
| User.current.delete_autologin_token(autologin) | |||
| end | |||
| User.current.delete_session_token(session[:tk]) | |||
| user = User.current | |||
| autologin = | |||
| if edu_setting('cookie_domain').present? | |||
| cookies.delete(autologin_cookie_name, domain: edu_setting('cookie_domain')) | |||
| else | |||
| cookies.delete(autologin_cookie_name) | |||
| end | |||
| user.delete_autologin_token(autologin) | |||
| user.delete_session_token(session[:tk]) | |||
| self.logged_user = nil | |||
| end | |||
| @@ -68,7 +73,7 @@ module LoginHelper | |||
| # Sets the logged in user | |||
| def logged_user=(user) | |||
| # reset_session | |||
| reset_session | |||
| if user && user.is_a?(User) | |||
| Rails.logger.info("########________logged_user___________###########{user.id}") | |||
| @@ -111,4 +116,34 @@ module LoginHelper | |||
| false | |||
| end | |||
| end | |||
| # TODO 同步token到trustie平台,保持同步登录状态 | |||
| def sync_user_token_to_trustie(login, token_value) | |||
| config = Rails.application.config_for(:configuration).symbolize_keys! | |||
| token = config[:sync_token] | |||
| api_host = config[:sync_url] | |||
| return if api_host.blank? | |||
| url = "#{api_host}/api/v1/users/sync_user_token" | |||
| sync_json = { | |||
| "token": token, | |||
| "login": login, | |||
| "user_token": token_value | |||
| } | |||
| uri = URI.parse(url) | |||
| if api_host | |||
| http = Net::HTTP.new(uri.hostname, uri.port) | |||
| if api_host.include?("https://") | |||
| http.use_ssl = true | |||
| end | |||
| http.send_request('POST', uri.path, sync_json.to_json, {'Content-Type' => 'application/json'}) | |||
| end | |||
| end | |||
| end | |||
| @@ -1,7 +1,7 @@ | |||
| module RegisterHelper | |||
| extend ActiveSupport::Concern | |||
| def autologin_register(username, email, password, platform= '') | |||
| def autologin_register(username, email, password, platform= 'forge') | |||
| result = {message: nil, user: nil} | |||
| email ||= "#{username@example.org}" | |||
| @@ -4,18 +4,18 @@ class HooksController < ApplicationController | |||
| before_action :check_user | |||
| before_action :set_repository | |||
| def index | |||
| hooks_response = Gitea::Hooks::ListService.new(@user, @repository.try(:identifier)).call | |||
| if hooks_response.status == 200 | |||
| def index | |||
| hooks_response = Gitea::Hooks::ListService.new(@user.gitea_token, @user.login, @repository.try(:identifier)).call | |||
| if hooks_response.status == 200 | |||
| lists = JSON.parse(hooks_response.body) | |||
| @hooks_size = lists.size | |||
| @hooks = paginate(lists) | |||
| else | |||
| else | |||
| normal_status(-1, "出现错误") | |||
| end | |||
| end | |||
| def create | |||
| def create | |||
| #根据gitea的api | |||
| # hook_params = { | |||
| # active: true, | |||
| @@ -36,17 +36,17 @@ class HooksController < ApplicationController | |||
| # content_type: params[:content_type].to_i, | |||
| # secret: params[:secret], | |||
| # events: { | |||
| # push_only: params[:push_only] || false, # 是否为推送事件 | |||
| # send_everything: params[:send_everything] || false, #是否为所有事件 | |||
| # push_only: params[:push_only] || false, # 是否为推送事件 | |||
| # send_everything: params[:send_everything] || false, #是否为所有事件 | |||
| # choose_events: params[:choose_events] || false, #是否为自定义事件 | |||
| # branch_filter: params[:branch_filter] || "*", | |||
| # events: { | |||
| # create: params[:create] || false, #创建分支/标签 | |||
| # delete: params[:delete] || false, #删除分支/标签 | |||
| # fork: params[:fork] || false, #仓库被派生 | |||
| # delete: params[:delete] || false, #删除分支/标签 | |||
| # fork: params[:fork] || false, #仓库被派生 | |||
| # issues: params[:issues] || false, #工单 | |||
| # issue_comment: params[:issue_comment] || false, #评论 | |||
| # push: params[:push] || false # 推送 | |||
| # issue_comment: params[:issue_comment] || false, #评论 | |||
| # push: params[:push] || false # 推送 | |||
| # pull_request: params[:pull_request] || false #合并请求 | |||
| # repository: params[:repository] || false #仓库 | |||
| # release: params[:release] || false #版本发布 | |||
| @@ -58,28 +58,28 @@ class HooksController < ApplicationController | |||
| Gitea::Hooks::CreateService.new(@user, @repository.try(:identifier), hook_params).call #创建gitea的hook功能 | |||
| Gitea::Hooks::CreateService.new(user, p.try(:identifier), hook_params).call #创建gitea的hook功能 | |||
| end | |||
| end | |||
| def update | |||
| def update | |||
| hook_params = params[:hook_params] | |||
| response = Gitea::Hooks::UpdateService.new(@user, @repository.try(:identifier), hook_params, params[:id]).call | |||
| if response.status == 200 | |||
| if response.status == 200 | |||
| normal_status(1, "更新成功") | |||
| else | |||
| else | |||
| normal_status(-1, "更新失败") | |||
| end | |||
| end | |||
| end | |||
| def destroy | |||
| def destroy | |||
| response = Gitea::Hooks::DestroyService.new(@user, @repository.try(:identifier), params[:id]).call | |||
| if response.status == 204 | |||
| normal_status(1, "删除成功") | |||
| else | |||
| else | |||
| normal_status(-1, "删除失败") | |||
| end | |||
| end | |||
| end | |||
| private | |||
| private | |||
| def set_repository | |||
| @repository = @project.repository | |||
| @@ -88,9 +88,9 @@ class HooksController < ApplicationController | |||
| normal_status(-1, "用户不存在") unless @user.present? | |||
| end | |||
| def check_user | |||
| unless @project.user_id == current_user.id | |||
| tip_exception(403, "您没有权限进入") | |||
| def check_user | |||
| unless @project.user_id == current_user.id | |||
| tip_exception(403, "您没有权限进入") | |||
| end | |||
| end | |||
| end | |||
| @@ -105,9 +105,6 @@ class IssuesController < ApplicationController | |||
| normal_status(-1, "标题不能为空") | |||
| elsif params[:subject].to_s.size > 255 | |||
| normal_status(-1, "标题不能超过255个字符") | |||
| elsif (params[:issue_type].to_s == "2") | |||
| return normal_status(-1, "悬赏的奖金必须大于0") if params[:token].to_i == 0 | |||
| else | |||
| issue_params = issue_send_params(params) | |||
| @@ -137,17 +134,16 @@ class IssuesController < ApplicationController | |||
| end | |||
| @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create") | |||
| # normal_status(0, "创建成功",) | |||
| render :json => { status: 0, message: "创建成功", id: @issue.id} | |||
| render json: {status: 0, message: "创建成", id: @issue.id} | |||
| else | |||
| normal_status(-1, @issue.errors.messages.values[0][0]) | |||
| end | |||
| end | |||
| end | |||
| def edit | |||
| # @all_branches = get_branches | |||
| # @issue_chosen = issue_left_chosen(@project, @issue.id) | |||
| @issue_attachments = @issue.attachments | |||
| end | |||
| @@ -204,7 +200,6 @@ class IssuesController < ApplicationController | |||
| else | |||
| normal_status(-1, @issue.errors.messages.values[0][0]) | |||
| end | |||
| end | |||
| def show | |||
| @@ -306,6 +301,9 @@ class IssuesController < ApplicationController | |||
| if type == 5 | |||
| @issue&.project_trends&.update_all(action_type: "close") | |||
| @issue.issue_times.update_all(end_time: Time.now) | |||
| if @issue.issue_type.to_s == "2" | |||
| post_to_chain("add", @issue.token, @issue.get_assign_user.try(:login)) | |||
| end | |||
| if @issue.issue_classify.to_s == "pull_request" | |||
| @issue&.pull_request&.update_attribute(:status, 2) | |||
| end | |||
| @@ -399,17 +397,6 @@ class IssuesController < ApplicationController | |||
| tracker_array | |||
| end | |||
| def get_branches | |||
| all_branches = [] | |||
| get_all_branches = Gitea::Repository::Branches::ListService.new(@user, @project&.repository.try(:identifier)).call | |||
| if get_all_branches && get_all_branches.size > 0 | |||
| get_all_branches.each do |b| | |||
| all_branches.push(b["name"]) | |||
| end | |||
| end | |||
| all_branches | |||
| end | |||
| def issue_send_params(params) | |||
| { | |||
| subject: params[:subject], | |||
| @@ -2,7 +2,7 @@ class MainController < ApplicationController | |||
| protect_from_forgery except: :index | |||
| skip_before_action :check_sign | |||
| skip_before_action :user_setup | |||
| # skip_before_action :setup_laboratory | |||
| skip_before_action :setup_laboratory | |||
| def first_stamp | |||
| render :json => { status: 0, message: Time.now.to_i } | |||
| @@ -7,7 +7,7 @@ class Oauth::EducoderController < Oauth::BaseController | |||
| token = params[:token] | |||
| ::OauthEducoderForm.new({login: login, token: token, callback_url: callback_url}).validate! | |||
| open_user= OpenUsers::Educoder.find_by(uid: login) | |||
| if open_user.present? && open_user.user.present? && open_user.user.email_binded? | |||
| @@ -17,15 +17,15 @@ class Oauth::EducoderController < Oauth::BaseController | |||
| redirect_to callback_url | |||
| else | |||
| Rails.logger.info "######## open user not exits" | |||
| user = User.find_by('login = ? or mail = ?', login, mail) | |||
| user = User.find_by(login: login) || User.find_by(mail: mail) | |||
| if user.is_a?(User) | |||
| if user.is_a?(User) && !user.is_a?(AnonymousUser) | |||
| OpenUsers::Educoder.create!(user: user, uid: login) | |||
| successful_authentication(user) | |||
| redirect_to callback_url | |||
| else | |||
| redirect_to oauth_register_path(login: login, callback_url: callback_url) | |||
| redirect_to oauth_register_path(login: login, mail: mail, callback_url: callback_url) | |||
| end | |||
| end | |||
| rescue WechatOauth::Error => ex | |||
| @@ -40,8 +40,5 @@ class PraiseTreadController < ApplicationController | |||
| end | |||
| private | |||
| def render_result | |||
| end | |||
| end | |||
| @@ -6,16 +6,6 @@ class ProjectCategoriesController < ApplicationController | |||
| end | |||
| def group_list | |||
| # if current_user&.logged? | |||
| # projects = Project.list_user_projects(current_user.id) | |||
| # else | |||
| # projects = Project.visible | |||
| # end | |||
| @project_children_categories = ProjectCategory.descendants | |||
| projects = Project.no_anomory_projects.visible | |||
| categories = projects.joins(:project_category).where(project_categories: {ancestry: nil}).group("project_categories.name", "project_categories.id").size.keys.to_h | |||
| extra_category_id = categories.delete("其他") | |||
| categories = categories.merge({"其他": extra_category_id}) if extra_category_id.present? #其他的放在最后面 | |||
| @category_group_list = categories | |||
| @project_categories = ProjectCategory.where('projects_count > 0').order(projects_count: :desc) | |||
| end | |||
| end | |||
| @@ -2,16 +2,26 @@ class ProjectsController < ApplicationController | |||
| include ApplicationHelper | |||
| include OperateProjectAbilityAble | |||
| include ProjectsHelper | |||
| before_action :require_login, except: %i[index branches group_type_list simple show fork_users praise_users watch_users] | |||
| before_action :load_project, except: %i[index group_type_list migrate create] | |||
| before_action :require_login, except: %i[index branches group_type_list simple show fork_users praise_users watch_users recommend about] | |||
| before_action :load_project, except: %i[index group_type_list migrate create recommend] | |||
| before_action :authorizate_user_can_edit_project!, only: %i[update] | |||
| before_action :project_public?, only: %i[fork_users praise_users watch_users] | |||
| def index | |||
| scope = Projects::ListQuery.call(params) | |||
| @projects = kaminari_paginate(scope) | |||
| @total_count = @projects.total_count | |||
| # @projects = kaminari_paginate(scope) | |||
| @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, owner: :user_extension) | |||
| 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? | |||
| else | |||
| cate = ProjectCategory.find_by(id: category_id) | |||
| cate&.projects_count || 0 | |||
| end | |||
| end | |||
| def create | |||
| @@ -34,7 +44,7 @@ class ProjectsController < ApplicationController | |||
| end | |||
| def branches | |||
| @branches = Gitea::Repository::Branches::ListService.new(@project.owner, @project.identifier).call | |||
| @branches = @project.forge? ? Gitea::Repository::Branches::ListService.new(@owner, @project.identifier).call : [] | |||
| end | |||
| def group_type_list | |||
| @@ -52,9 +62,13 @@ class ProjectsController < ApplicationController | |||
| ActiveRecord::Base.transaction do | |||
| # Projects::CreateForm.new(project_params).validate! | |||
| private = params[:private] | |||
| gitea_params = { | |||
| private: private, | |||
| default_branch: params[:default_branch] | |||
| } | |||
| if [true, false].include? private | |||
| new_project_params = project_params.merge(is_public: !private) | |||
| Gitea::Repository::UpdateService.new(@project.owner, @project.repository.identifier, {private: private}).call | |||
| Gitea::Repository::UpdateService.call(@owner, @project.identifier, gitea_params) | |||
| @project.repository.update_column(:hidden, private) | |||
| end | |||
| @project.update_attributes!(new_project_params) | |||
| @@ -95,13 +109,44 @@ class ProjectsController < ApplicationController | |||
| end | |||
| def fork_users | |||
| fork_users = @project.fork_users.includes(:user, :project).order("fork_users.created_at desc").distinct | |||
| fork_users = @project.fork_users.includes(:user, :project, :fork_project).order("fork_users.created_at desc").distinct | |||
| @forks_count = fork_users.size | |||
| @fork_users = paginate(fork_users) | |||
| end | |||
| def simple | |||
| json_response(@project) | |||
| json_response(@project, current_user) | |||
| end | |||
| def recommend | |||
| @projects = Project.recommend.includes(:repository, :project_category, owner: :user_extension).limit(5) | |||
| end | |||
| def about | |||
| @project_detail = @project.project_detail | |||
| @attachments = Array(@project_detail&.attachments) if request.get? | |||
| ActiveRecord::Base.transaction do | |||
| if request.post? | |||
| require_login | |||
| authorizate_user_can_edit_project! | |||
| unless @project_detail.present? | |||
| @project_detail = ProjectDetail.new( | |||
| content: params[:content], | |||
| project_id: @project.id) | |||
| else | |||
| @project_detail.content = params[:content] | |||
| end | |||
| if @project_detail.save! | |||
| attachment_ids = Array(params[:attachment_ids]) | |||
| @attachments = Attachment.where(id: attachment_ids) | |||
| @attachments.update_all( | |||
| container_id: @project_detail.id, | |||
| container_type: @project_detail.model_name.name, | |||
| author_id: current_user.id, | |||
| description: "") | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,43 @@ | |||
| class ProtectedBranchesController < ApplicationController | |||
| include OperateProjectAbilityAble | |||
| before_action :require_login | |||
| before_action :load_repository | |||
| before_action :authorizate_user_can_edit_project! | |||
| def index | |||
| scope = @repository.protected_branches | |||
| @total_count = scope.size | |||
| @protected_branches = paginate(scope) | |||
| end | |||
| def create | |||
| @protected_branch = ProtectedBranches::CreateService.call(@repository, @owner, params) | |||
| render_protected_branch_json | |||
| end | |||
| def update | |||
| @protected_branch = ProtectedBranches::UpdateService.call(@repository, @owner, params) | |||
| end | |||
| def destroy | |||
| ProtectedBranches::DestroyService.call(@repository, @owner, params[:branch_name]) | |||
| render_ok | |||
| end | |||
| def show | |||
| @protected_branch = ProtectedBranches::GetService.call(@repository, @owner, params) | |||
| end | |||
| def edit | |||
| @branch, @protected_branch = ProtectedBranches::EditService.call(@repository, @owner, params[:branch_name]) | |||
| end | |||
| private | |||
| def render_protected_branch_json | |||
| @protected_branch.persisted? ? @protected_branch : render_error('创建失败!') | |||
| end | |||
| end | |||
| @@ -1,9 +1,8 @@ | |||
| class PullRequestsController < ApplicationController | |||
| before_action :require_login, except: [:index, :show] | |||
| before_action :require_login, except: [:index, :show, :files, :commits] | |||
| before_action :load_repository | |||
| before_action :set_user, only: [:new, :get_branches] | |||
| before_action :find_pull_request, except: [:index, :new, :create, :check_can_merge,:get_branches,:create_merge_infos] | |||
| # before_action :get_relatived, only: [:edit] | |||
| before_action :find_pull_request, except: [:index, :new, :create, :check_can_merge,:get_branches,:create_merge_infos, :files, :commits] | |||
| before_action :load_pull_request, only: [:files, :commits] | |||
| include TagChosenHelper | |||
| include ApplicationHelper | |||
| @@ -24,11 +23,11 @@ class PullRequestsController < ApplicationController | |||
| end | |||
| def new | |||
| @all_branches = PullRequests::BranchesService.new(@user, @project).call | |||
| @all_branches = Branches::ListService.call(@owner, @project) | |||
| @is_fork = @project.forked_from_project_id.present? | |||
| @projects_names = [{ | |||
| project_user_login: @user.try(:login), | |||
| project_name: "#{@user.try(:show_real_name)}/#{@repository.try(:identifier)}", | |||
| project_user_login: @owner.try(:login), | |||
| project_name: "#{@owner.try(:show_real_name)}/#{@repository.try(:identifier)}", | |||
| project_id: @project.identifier, | |||
| id: @project.id | |||
| }] | |||
| @@ -45,66 +44,20 @@ class PullRequestsController < ApplicationController | |||
| end | |||
| def get_branches | |||
| branch_result = PullRequests::BranchesService.new(@user, @project).call | |||
| branch_result = Branches::ListService.call(@owner, @project) | |||
| render json: branch_result | |||
| # return json: branch_result | |||
| end | |||
| def create | |||
| if params[:title].nil? | |||
| normal_status(-1, "名称不能为空") | |||
| elsif params[:issue_tag_ids].nil? | |||
| normal_status(-1, "标签不能为空") | |||
| else | |||
| ActiveRecord::Base.transaction do | |||
| begin | |||
| merge_params | |||
| pull_issue = Issue.new(@issue_params) | |||
| if pull_issue.save! | |||
| pr_params = { | |||
| user_id: current_user.try(:id), | |||
| project_id: @project.id, | |||
| issue_id: pull_issue.id, | |||
| fork_project_id: params[:fork_project_id], | |||
| is_original: params[:is_original] | |||
| } | |||
| local_requests = PullRequest.new(@local_params.merge(pr_params)) | |||
| if local_requests.save | |||
| remote_pr_params = @local_params | |||
| remote_pr_params = remote_pr_params.merge(head: "#{params[:merge_user_login]}:#{params[:head]}").compact if local_requests.is_original && params[:merge_user_login] | |||
| gitea_request = Gitea::PullRequest::CreateService.call(current_user.try(:gitea_token), @project.owner, @repository.try(:identifier), remote_pr_params.except(:milestone)) | |||
| if gitea_request && local_requests.update_attributes(gpid: gitea_request["number"]) | |||
| if params[:issue_tag_ids].present? | |||
| params[:issue_tag_ids].each do |tag| | |||
| IssueTagsRelate.create!(issue_id: pull_issue.id, issue_tag_id: tag) | |||
| end | |||
| end | |||
| if params[:assigned_to_id].present? | |||
| Tiding.create!(user_id: params[:assigned_to_id], trigger_user_id: current_user.id, | |||
| container_id: local_requests.id, container_type: 'PullRequest', | |||
| parent_container_id: @project.id, parent_container_type: "Project", | |||
| tiding_type: 'pull_request', status: 0) | |||
| end | |||
| local_requests.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create") | |||
| if params[:title].to_s.include?("WIP:") | |||
| pull_issue.custom_journal_detail("WIP", "", "这个合并请求被标记为尚未完成的工作。完成后请从标题中移除WIP:前缀。", current_user&.id) | |||
| end | |||
| # render :json => { status: 0, message: "PullRequest创建成功", id: pull_issue.id} | |||
| normal_status(0, "PullRequest创建成功") | |||
| else | |||
| normal_status(-1, "PullRequest创建失败") | |||
| end | |||
| else | |||
| normal_status(-1, local_requests.errors.messages.values[0][0]) | |||
| end | |||
| else | |||
| normal_status(-1, pull_issue.errors.messages.values[0][0]) | |||
| end | |||
| rescue => e | |||
| normal_status(-1, e.message) | |||
| raise ActiveRecord::Rollback | |||
| end | |||
| ActiveRecord::Base.transaction do | |||
| @pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params) | |||
| if @gitea_pull_request[:status] == :success | |||
| @pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"]) | |||
| render_ok | |||
| else | |||
| render_error("create pull request error: #{@gitea_pull_request[:status]}") | |||
| raise ActiveRecord::Rollback | |||
| end | |||
| end | |||
| end | |||
| @@ -226,11 +179,11 @@ class PullRequestsController < ApplicationController | |||
| elsif target_head === target_base && !is_original | |||
| normal_status(-2, "分支内容相同,无需创建合并请求") | |||
| else | |||
| can_merge = @project&.pull_requests.where(user_id: current_user&.id, head: target_head, base: target_base, status: 0, is_original: is_original, fork_project_id: params[:fork_project_id]) | |||
| can_merge = @project&.pull_requests.where(head: target_head, base: target_base, status: 0, is_original: is_original, fork_project_id: params[:fork_project_id]) | |||
| if can_merge.present? | |||
| render json: { | |||
| status: -2, | |||
| message: "在这些分支之间的合并请求已存在:<a href='/projects/#{@project.id}/merge/#{can_merge.first.id}/Messagecount''>#{can_merge.first.try(:title)}</a>", | |||
| message: "在这些分支之间的合并请求已存在:<a href='/projects/#{@owner.login}/#{@project.identifier}/pulls/#{can_merge.first.id}/Messagecount''>#{can_merge.first.try(:title)}</a>", | |||
| } | |||
| else | |||
| normal_status(0, "可以合并") | |||
| @@ -239,9 +192,19 @@ class PullRequestsController < ApplicationController | |||
| end | |||
| def files | |||
| @files_result = Gitea::PullRequest::FilesService.call(@owner.login, @project.identifier, @pull_request.gpid) | |||
| # render json: @files_result | |||
| end | |||
| def commits | |||
| @commits_result = Gitea::PullRequest::CommitsService.call(@owner.login, @project.identifier, @pull_request.gpid) | |||
| # render json: @commits_result | |||
| end | |||
| private | |||
| def set_user | |||
| @user = @project.owner | |||
| def load_pull_request | |||
| @pull_request = PullRequest.find params[:id] | |||
| end | |||
| def find_pull_request | |||
| @@ -13,7 +13,7 @@ class RepositoriesController < ApplicationController | |||
| def show | |||
| @user = current_user | |||
| @repo = @project.repository | |||
| @result = Gitea::Repository::GetService.new(@project.owner, @project.identifier).call | |||
| @result = @project.forge? ? Gitea::Repository::GetService.new(@owner, @project.identifier).call : nil | |||
| @project_fork_id = @project.try(:forked_from_project_id) | |||
| if @project_fork_id.present? | |||
| @fork_project = Project.find_by(id: @project_fork_id) | |||
| @@ -26,60 +26,85 @@ class RepositoriesController < ApplicationController | |||
| def entries | |||
| @project.increment!(:visits) | |||
| @project_owner = @project.owner | |||
| @entries = Gitea::Repository::Entries::ListService.new(@project_owner, @project.identifier, ref: @ref).call | |||
| @entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : [] | |||
| @path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/" | |||
| if @project.educoder? | |||
| @entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name) | |||
| else | |||
| @entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call | |||
| @entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : [] | |||
| @path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/" | |||
| end | |||
| end | |||
| def top_counts | |||
| @result = Gitea::Repository::GetService.new(@project.owner, @project.identifier).call | |||
| @result = @project.educoder? ? nil : Gitea::Repository::GetService.new(@project.owner, @project.identifier).call | |||
| end | |||
| def sub_entries | |||
| file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) | |||
| interactor = Repositories::EntriesInteractor.call(@project.owner, @project.identifier, file_path_uri, ref: @ref) | |||
| if interactor.success? | |||
| @sub_entries = interactor.result | |||
| @sub_entries = [] << @sub_entries unless @sub_entries.is_a? Array | |||
| @sub_entries = @sub_entries.sort_by{ |hash| hash['type'] } | |||
| if @project.educoder? | |||
| if params[:type] === 'file' | |||
| @sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri) | |||
| logger.info "######### sub_entries: #{@sub_entries}" | |||
| return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1 | |||
| tmp_entries = [{ | |||
| "content" => @sub_entries['data']['content'], | |||
| "type" => "blob" | |||
| }] | |||
| @sub_entries = { | |||
| "trees"=>tmp_entries, | |||
| "commits" => [{}] | |||
| } | |||
| else | |||
| @sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri}) | |||
| end | |||
| else | |||
| render_error(interactor.error) | |||
| interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref) | |||
| if interactor.success? | |||
| result = interactor.result | |||
| @sub_entries = result.is_a?(Array) ? result.sort_by{ |hash| hash['type'] } : result | |||
| else | |||
| render_error(interactor.error) | |||
| end | |||
| end | |||
| end | |||
| def commits | |||
| @project_owner = @project.owner | |||
| @hash_commit = Gitea::Repository::Commits::ListService.new(@project_owner.login, @project.identifier, | |||
| @hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier, | |||
| sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call | |||
| end | |||
| def commit | |||
| @commit = Gitea::Repository::Commits::GetService.new(@repository.user.login, @repository.identifier, params[:sha], current_user.gitea_token).call | |||
| @sha = params[:sha] | |||
| @commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token) | |||
| @commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true}) | |||
| end | |||
| def tags | |||
| @tags = Gitea::Repository::Tags::ListService.new(current_user&.gitea_token, @project.owner.login, @project.identifier, {page: params[:page], limit: params[:limit]}).call | |||
| @tags = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]}) | |||
| end | |||
| def edit | |||
| end | |||
| def create_file | |||
| interactor = Gitea::CreateFileInteractor.call(current_user.gitea_token, @project.owner.login, content_params) | |||
| interactor = Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params) | |||
| if interactor.success? | |||
| @file = interactor.result | |||
| create_new_pr(params) | |||
| # create_new_pr(params) | |||
| else | |||
| render_error(interactor.error) | |||
| end | |||
| end | |||
| def update_file | |||
| interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @project.owner.login, params.merge(identifier: @project.identifier)) | |||
| interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier)) | |||
| if interactor.success? | |||
| @file = interactor.result | |||
| create_new_pr(params) | |||
| # TODO: 是否创建pr | |||
| # create_new_pr(params) | |||
| render_result(1, "更新成功") | |||
| else | |||
| render_error(interactor.error) | |||
| @@ -87,7 +112,7 @@ class RepositoriesController < ApplicationController | |||
| end | |||
| def delete_file | |||
| interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @project.owner.login, params.merge(identifier: @project.identifier)) | |||
| interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier)) | |||
| if interactor.success? | |||
| @file = interactor.result | |||
| render_result(1, "文件删除成功") | |||
| @@ -133,8 +158,8 @@ class RepositoriesController < ApplicationController | |||
| end | |||
| def get_statistics | |||
| @branches_count = Gitea::Repository::Branches::ListService.new(@project.owner, @project.identifier).call&.size | |||
| @tags_count = Gitea::Repository::Tags::ListService.new(current_user&.gitea_token, @project.owner.login, @project.identifier).call&.size | |||
| @branches_count = @project.educoder? ? 0 : Gitea::Repository::Branches::ListService.new(@project.owner, @project.identifier).call&.size | |||
| @tags_count = @project.educoder? ? 0 : Gitea::Repository::Tags::ListService.new(current_user&.gitea_token, @project.owner.login, @project.identifier).call&.size | |||
| end | |||
| def get_ref | |||
| @@ -142,9 +167,9 @@ class RepositoriesController < ApplicationController | |||
| end | |||
| def get_latest_commit | |||
| latest_commit = project_commits | |||
| @latest_commit = latest_commit[:body][0] if latest_commit.present? | |||
| @commits_count = latest_commit[:total_count] if latest_commit.present? | |||
| latest_commit = @project.educoder? ? nil : project_commits | |||
| @latest_commit = latest_commit.present? ? latest_commit[:body][0] : nil | |||
| @commits_count = latest_commit.present? ? latest_commit[:total_count] : 0 | |||
| end | |||
| def content_params | |||
| @@ -154,6 +179,10 @@ class RepositoriesController < ApplicationController | |||
| new_branch: params[:new_branch], | |||
| content: params[:content], | |||
| message: params[:message], | |||
| committer: { | |||
| email: current_user.mail, | |||
| name: current_user.login | |||
| }, | |||
| identifier: @project.identifier | |||
| } | |||
| end | |||
| @@ -208,14 +237,14 @@ class RepositoriesController < ApplicationController | |||
| issue_type: "1", | |||
| tracker_id: 2, | |||
| status_id: 1, | |||
| priority_id: 1 | |||
| priority_id: params[:priority_id] || "2" | |||
| } | |||
| @pull_issue = Issue.new(issue_params) | |||
| if @pull_issue.save! | |||
| local_requests = PullRequest.new(local_params.merge(user_id: current_user.try(:id), project_id: @project.id, issue_id: @pull_issue.id)) | |||
| if local_requests.save | |||
| gitea_request = Gitea::PullRequest::CreateService.new(current_user.try(:gitea_token), @project.owner, @project.try(:identifier), requests_params).call | |||
| if gitea_request && local_requests.update_attributes(gpid: gitea_request["number"]) | |||
| gitea_request = Gitea::PullRequest::CreateService.new(current_user.try(:gitea_token), @owner.login, @project.try(:identifier), requests_params).call | |||
| if gitea_request[:status] == :success && local_requests.update_attributes(gpid: gitea_request["body"]["number"]) | |||
| local_requests.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create") | |||
| end | |||
| end | |||
| @@ -1,8 +1,11 @@ | |||
| class UsersController < ApplicationController | |||
| include Ci::DbConnectable | |||
| before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users] | |||
| before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users] | |||
| before_action :require_login, only: %i[me list] | |||
| before_action :connect_to_ci_database, only: :get_user_info, if: -> { current_user && !current_user.is_a?(AnonymousUser) && current_user.devops_certification? } | |||
| skip_before_action :check_sign, only: [:attachment_show] | |||
| def list | |||
| @@ -139,7 +139,7 @@ class VersionReleasesController < ApplicationController | |||
| body: params[:body], | |||
| draft: params[:draft] || false, | |||
| name: params[:name], | |||
| prerelease: params[:prerelease], | |||
| prerelease: params[:prerelease] || false, | |||
| tag_name: params[:tag_name], | |||
| target_commitish: params[:target_commitish] || "master" #分支 | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| class Ci::CreateCloudAccountForm | |||
| include ActiveModel::Model | |||
| attr_accessor :ip_num, :account, :secret | |||
| # validates :project_id, :account, :secret, presence: true | |||
| validates :account, :secret, presence: true | |||
| validates :ip_num, presence: true, format: { with: CustomRegexp::IP, multiline: true, message: 'IP 地址格式不对' } | |||
| end | |||
| @@ -0,0 +1,19 @@ | |||
| class ProtectedBranches::CreateForm < BaseForm | |||
| attr_accessor :repository, :branch_name, :can_push, :enable_whitelist, :whitelist_user_i_ds, | |||
| :whitelist_team_i_ds, :enable_merge_whitelist, :whitelist_deploy_keys, :merge_whitelist_user_i_ds, | |||
| :merge_whitelist_team_i_ds, :enable_status_check, :status_check_contexts, :approvals_whitelist_user_i_ds, | |||
| :approvals_whitelist_team_i_ds, :required_approvals, :enable_approvals_whitelist, :block_on_rejected_reviews, | |||
| :dismiss_stale_approvals, :require_signed_commits, :protected_file_patterns, :block_on_outdated_branch | |||
| validates :repo_id, :branch_name, presence: true | |||
| validate do | |||
| check_branch_name! | |||
| end | |||
| def check_branch_name! | |||
| protected_branch_exists = repository.protected_branches.exists?(branch_name) | |||
| raise "Protected branch '#{branch_name}' already exists" if protected_branch_exists | |||
| end | |||
| end | |||
| @@ -436,4 +436,9 @@ module ApplicationHelper | |||
| def find_user_by_login(login) | |||
| User.find_by_login login | |||
| end | |||
| def render_base64_decoded(str) | |||
| return nil if str.blank? | |||
| Base64.decode64 str | |||
| end | |||
| end | |||
| @@ -0,0 +1,11 @@ | |||
| module Ci::BuildsHelper | |||
| def format_utc_time(unix_time) | |||
| return nil if unix_time == 0 | |||
| Time.at(unix_time).strftime("%Y-%m-%d %H:%M") | |||
| end | |||
| def render_duartion_time(end_time, start_time) | |||
| return nil if end_time == 0 || start_time == 0 | |||
| game_spend_time(end_time - start_time) | |||
| end | |||
| end | |||
| @@ -0,0 +1,2 @@ | |||
| module Ci::LanguagesHelper | |||
| end | |||
| @@ -0,0 +1,2 @@ | |||
| module CompareHelper | |||
| end | |||
| @@ -0,0 +1,2 @@ | |||
| module DevOpsHelper | |||
| end | |||
| @@ -1,2 +1,18 @@ | |||
| module MembersHelper | |||
| def get_user_token(user_login,reponame) | |||
| query_params = { | |||
| type: "query", | |||
| chain_params: { | |||
| reponame: reponame, | |||
| username: user_login | |||
| } | |||
| } | |||
| response = Gitea::Chain::ChainGetService.new(query_params).call | |||
| if response.status == 200 | |||
| return JSON.parse(response.body)["balance"].to_i | |||
| else | |||
| return 0 | |||
| end | |||
| end | |||
| end | |||
| @@ -28,8 +28,10 @@ module ProjectsHelper | |||
| (User.find_by_login identifier) || (User.find_by_mail identifier) | |||
| end | |||
| def json_response(project) | |||
| repo = project.repository | |||
| def json_response(project, user) | |||
| # repo = project.repository | |||
| repo = Repository.includes(:mirror).select(:id, :mirror_url).find_by(project: project) | |||
| tmp_json = {} | |||
| unless project.common? | |||
| tmp_json = tmp_json.merge({ | |||
| @@ -41,16 +43,48 @@ module ProjectsHelper | |||
| end | |||
| tmp_json = tmp_json.merge({ | |||
| identifier: project.identifier, | |||
| identifier: render_identifier(project), | |||
| name: project.name, | |||
| platform: project.platform, | |||
| id: project.id, | |||
| repo_id: repo.id, | |||
| open_devops: (user.blank? || user.is_a?(AnonymousUser)) ? false : project.open_devops?, | |||
| type: project.numerical_for_project_type, | |||
| author: { | |||
| login: project.owner.login, | |||
| name: project.owner.real_name, | |||
| image_url: url_to_avatar(project.owner) | |||
| } | |||
| author: render_owner(project) | |||
| }).compact | |||
| render json: tmp_json | |||
| end | |||
| def render_owner(project) | |||
| if project.educoder? | |||
| { | |||
| login: project.project_educoder.owner, | |||
| name: project.project_educoder.owner, | |||
| image_url: project.project_educoder.image_url | |||
| } | |||
| else | |||
| { | |||
| login: @owner.login, | |||
| name: @owner.real_name, | |||
| image_url: url_to_avatar(@owner) | |||
| } | |||
| end | |||
| end | |||
| def render_identifier(project) | |||
| project.educoder? ? project.project_educoder&.repo_name&.split('/')[1] : project.identifier | |||
| end | |||
| def render_author(project) | |||
| project.educoder? ? project.project_educoder&.repo_name&.split('/')[0] : project.owner.login | |||
| end | |||
| def render_educoder_avatar_url(project_educoder) | |||
| [Rails.application.config_for(:configuration)['educoder']['cdn_url'], project_educoder&.image_url].join('/') | |||
| end | |||
| def render_avatar_url(owner) | |||
| ['images', url_to_avatar(owner)].join('/') | |||
| end | |||
| end | |||
| @@ -0,0 +1,2 @@ | |||
| module ProtectedBranchesHelper | |||
| end | |||
| @@ -1,11 +1,17 @@ | |||
| module RepositoriesHelper | |||
| def render_permission(user, project) | |||
| return "Admin" if user&.admin? | |||
| return "Owner" if user === project.owner | |||
| project.get_premission(user) | |||
| end | |||
| def render_decode64_content(str) | |||
| return nil if str.blank? | |||
| Base64.decode64(str).force_encoding('UTF-8') | |||
| Base64.decode64(str).force_encoding("UTF-8") | |||
| end | |||
| def download_type(str) | |||
| default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb png jpg gif tif psd svg RData rdata) | |||
| default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb png jpg gif tif psd svg RData rdata doc docx mpp vsdx) | |||
| default_type.include?(str&.downcase) | |||
| end | |||
| @@ -44,7 +50,7 @@ module RepositoriesHelper | |||
| end | |||
| if r_content.include?("?") | |||
| new_r_content = r_content + "&raw=true" | |||
| else | |||
| else | |||
| new_r_content = r_content + "?raw=true" | |||
| end | |||
| unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:") | |||
| @@ -56,4 +62,14 @@ module RepositoriesHelper | |||
| return content | |||
| end | |||
| # unix_time values for example: 1604382982 | |||
| def render_format_time_with_unix(unix_time) | |||
| Time.at(unix_time).strftime("%Y-%m-%d %H:%M") | |||
| end | |||
| # date for example: 2020-11-01T19:57:27+08:00 | |||
| def render_format_time_with_date(date) | |||
| date.to_time.strftime("%Y-%m-%d %H:%M") | |||
| end | |||
| end | |||
| @@ -1,4 +1,143 @@ | |||
| module TagChosenHelper | |||
| def get_associated_data(project) | |||
| issue_comment_users_array = [] | |||
| cost_time_array = [] | |||
| all_issues = [] | |||
| { | |||
| "assign_user": render_cache_collaborators(project), | |||
| "tracker": render_cache_trackers, | |||
| "issue_status": render_cache_issue_statuses, | |||
| "priority": render_cache_issue_priorities, | |||
| "issue_version": render_cache_milestones(project), | |||
| "start_date": "", | |||
| "due_date": "", | |||
| "joins_users": issue_comment_users_array, | |||
| "cost_time_users": cost_time_array, | |||
| # "total_cost_time": Time.at(all_cost_time).utc.strftime('%H h %M min %S s'), | |||
| # "be_depended_issues": be_depended_issues_array, | |||
| # "depended_issues":depended_issues_array, | |||
| # "estimated_hours": issue_info[7], | |||
| "done_ratio": render_complete_percentage, | |||
| "issue_tag": render_issue_tags(project), | |||
| "issue_type": render_issue_species, | |||
| "all_issues": all_issues | |||
| } | |||
| end | |||
| def render_cache_trackers | |||
| cache_key = "all_trackers/#{Tracker.maximum('id')}" | |||
| Rails.cache.fetch(cache_key) do | |||
| Tracker.select(:id, :name, :position).collect do |event| | |||
| { | |||
| id: event.id, | |||
| name: event.name, | |||
| position: event.position, | |||
| is_chosen: '0' | |||
| } | |||
| end | |||
| end | |||
| end | |||
| def render_cache_issue_statuses | |||
| cache_key = "all_issue_statuses/#{IssueStatus.maximum('id')}" | |||
| Rails.cache.fetch(cache_key) do | |||
| IssueStatus.select(:id, :name, :position).collect do |event| | |||
| { | |||
| id: event.id, | |||
| name: event.name, | |||
| position: event.position, | |||
| is_chosen: '0' | |||
| } | |||
| end | |||
| end | |||
| end | |||
| def render_cache_issue_priorities | |||
| cache_key = "all_issue_priorities/#{IssuePriority.maximum('id')}" | |||
| Rails.cache.fetch(cache_key) do | |||
| IssuePriority.select(:id, :name, :position).collect do |event| | |||
| { | |||
| id: event.id, | |||
| name: event.name, | |||
| position: event.position, | |||
| is_chosen: '0' | |||
| } | |||
| end | |||
| end | |||
| end | |||
| def render_complete_percentage | |||
| completion_nums = %w(0 10 20 30 40 50 60 70 80 90 100) | |||
| completion_nums.collect do |event| | |||
| { | |||
| id: event.to_i, | |||
| name: event + "%", | |||
| is_chosen: '0' | |||
| } | |||
| end | |||
| end | |||
| def render_issue_species | |||
| species = %W(普通 悬赏) | |||
| species.collect do |event| | |||
| { | |||
| id: event.to_i + 1, | |||
| token: nil, | |||
| is_chosen: '0' | |||
| } | |||
| end | |||
| end | |||
| def render_issue_tags(project) | |||
| # project.issue_tags.last&.cache_key | |||
| cache_key = "all_issue_tags/#{project.issue_tags.maximum('updated_at')}" | |||
| Rails.cache.fetch(cache_key) do | |||
| project.issue_tags.select(:id, :name, :color).collect do |event| | |||
| { | |||
| id: event.id, | |||
| name: event.name, | |||
| color: event.color, | |||
| is_chosen: '0' | |||
| } | |||
| end | |||
| end | |||
| end | |||
| def render_cache_milestones(project) | |||
| cache_key = "all_milestones/#{project.versions.maximum('updated_on')}" | |||
| Rails.cache.fetch(cache_key) do | |||
| project.versions.select(:id, :name, :status).collect do |event| | |||
| { | |||
| id: event.id, | |||
| name: event.name, | |||
| status: event.status, | |||
| is_chosen: '0' | |||
| } | |||
| end | |||
| end | |||
| end | |||
| def render_cache_collaborators(project) | |||
| cache_key = "all_collaborators/#{project.members.maximum('created_on')}" | |||
| Rails.cache.fetch(cache_key) do | |||
| project.members.includes(:user).collect do |event| | |||
| { | |||
| id: event.user&.id, | |||
| name: event.user&.show_real_name, | |||
| avatar_url: url_to_avatar(event.user), | |||
| is_chosen: '0' | |||
| } | |||
| end | |||
| end | |||
| end | |||
| def issue_left_chosen(project,issue_id) | |||
| issue_info = Array.new(11) | |||
| @@ -205,4 +344,4 @@ module TagChosenHelper | |||
| # be_depended_issues_array | |||
| # end | |||
| end | |||
| end | |||
| @@ -9,7 +9,8 @@ module Gitea | |||
| attr_reader :error, :result | |||
| def initialize(token, owner, params) | |||
| @owner = owner | |||
| @token = token | |||
| @owner = owner | |||
| @params = params | |||
| end | |||
| @@ -58,6 +59,7 @@ module Gitea | |||
| 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(message: @params[:message]) unless @params[:message].blank? | |||
| file_params = file_params.merge(committer: @params[:committer]) | |||
| file_params | |||
| end | |||
| end | |||
| @@ -22,7 +22,7 @@ module Gitea | |||
| def run | |||
| Gitea::UserForm.new(params).validate! | |||
| response = Gitea::User::RegisterService.new(params).call | |||
| response = Gitea::User::RegisterService.call(params.merge(token: token)) | |||
| render_result(response) | |||
| rescue Exception => exception | |||
| Rails.logger.info "Exception ===========> #{exception.message}" | |||
| @@ -41,5 +41,12 @@ module Gitea | |||
| def render_result(response) | |||
| @result = response | |||
| end | |||
| def token | |||
| { | |||
| username: Gitea.gitea_config[:access_key_id], | |||
| password: Gitea.gitea_config[:access_key_secret] | |||
| } | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,44 @@ | |||
| module Repositories::ProtectedBranches | |||
| class DeleteInteractor | |||
| def self.call(user, identifier, filepath, **args) | |||
| interactor = new(user, identifier, filepath, **args) | |||
| interactor.run | |||
| interactor | |||
| end | |||
| attr_reader :error, :result | |||
| def initialize(user, identifier, filepath, **args) | |||
| @user = user | |||
| @identifier = identifier | |||
| @filepath = filepath | |||
| @args = args | |||
| end | |||
| def success? | |||
| @error.nil? | |||
| end | |||
| def result | |||
| end | |||
| def run | |||
| rescue Exception => exception | |||
| fail!(exception.message) | |||
| end | |||
| private | |||
| attr_reader :user, :identifier, :filepath, :args | |||
| def fail!(error) | |||
| @error = error | |||
| end | |||
| def render_result(response) | |||
| @result = response | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,24 @@ | |||
| class PostChainJob < ApplicationJob | |||
| queue_as :default | |||
| def perform(chain_params) | |||
| status = false | |||
| chain_type = chain_params[:type].to_s | |||
| reponame = chain_params[:chain_params][:reponame] | |||
| 5.times do |i| | |||
| if status | |||
| break | |||
| else | |||
| response = Gitea::Chain::ChainPostService.new(chain_params).call | |||
| if response.status == 200 | |||
| reponse_body = response&.body | |||
| messages = reponse_body.present? ? JSON.parse(reponse_body) : "success" | |||
| status = true | |||
| Rails.logger.info("################_repository__#{reponame}______create_chain_success_try:_#{i+1}_message__:#{messages}__") | |||
| else | |||
| Rails.logger.info("########_repository__#{reponame}______create_chain_failed__try:_#{i+1}_") | |||
| end | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,73 @@ | |||
| require 'uri' | |||
| require 'net/http' | |||
| class SyncEducoderShixunJob < ApplicationJob | |||
| queue_as :default | |||
| def perform(url, private_token, page, per_page) | |||
| uri = URI("#{url}?page=#{page}&per_page=#{per_page}&private_token=#{private_token}") | |||
| puts "-------url: #{uri}" | |||
| response = Net::HTTP.get_response(uri) | |||
| result = JSON.parse(response.body) | |||
| if result['status'] != 0 | |||
| puts "======= 接口请求失败!" | |||
| return | |||
| end | |||
| result['data']['repositories'].each do |re| | |||
| next if re['repo_name'].blank? | |||
| next if ProjectEducoder.exists?(repo_name: re['repo_name']) | |||
| language = ProjectLanguage.find_by_name re['language'] | |||
| language = ProjectLanguage.create!(name: re['language']) if language.blank? | |||
| category = ProjectCategory.find_by_name re['category'] | |||
| category = ProjectCategory.create!(name: re['category']) if category.blank? | |||
| project_params = | |||
| { | |||
| name: re['name'], | |||
| # user_id: params[:user_id], | |||
| description: re['description'], | |||
| project_category_id: category.id, | |||
| project_language_id: language.id, | |||
| is_public: true, | |||
| # ignore_id: params[:ignore_id], | |||
| # license_id: params[:license_id], | |||
| identifier: re['repo_name'], | |||
| platform: Project.platforms[:educoder] | |||
| } | |||
| project = Project.new(project_params) | |||
| ActiveRecord::Base.transaction do | |||
| if project.save! | |||
| repo_params = | |||
| { | |||
| hidden: false, | |||
| project_id: project.id, | |||
| identifier: re['repo_name'] | |||
| } | |||
| ProjectEducoder.create!( | |||
| project_id: project.id, | |||
| owner: re['username'], | |||
| repo_name: re['repo_name'], | |||
| forked_count: re['forked_count'], | |||
| commit_count: re['commit_count'], | |||
| image_url: re['image_url']) | |||
| repo = Repository.new(repo_params) | |||
| repo.save! | |||
| puts "项目: #{re['name']} 同步成功" | |||
| else | |||
| puts "项目: #{re['name']} 同步失败" | |||
| end | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -8,7 +8,7 @@ class SyncProjectsJob < ApplicationJob | |||
| SyncLog.sync_log("==========begin to sync #{sync_params[:type]} to forge============") | |||
| SyncLog.sync_log("==========sync_params:#{sync_params}============") | |||
| begin | |||
| begin | |||
| url = "#{gitea_main}/sync_forges" #trustie上的相关路由 | |||
| uri = URI.parse(url) | |||
| http = Net::HTTP.new(uri.hostname, uri.port) | |||
| @@ -85,7 +85,7 @@ class SyncProjectsJob < ApplicationJob | |||
| new_target = target_type.constantize.new(re[:target_params].merge(user_id: u_id)) | |||
| end | |||
| end | |||
| if !is_exists && new_target.save! | |||
| SyncLog.sync_log("***【#{target_type}】. create_success---------------") | |||
| if re[:journals].present? | |||
| @@ -112,10 +112,8 @@ class SyncProjectsJob < ApplicationJob | |||
| SyncLog.sync_log("=========***【#{target_type}】creat_had_erros:#{e}===================") | |||
| next | |||
| end | |||
| end | |||
| end | |||
| def create_journals(target_jsons, target_type,issue_id) | |||
| @@ -127,7 +125,7 @@ class SyncProjectsJob < ApplicationJob | |||
| if re[:target_params].present? | |||
| u_id = User.select(:id, :login).where(login: re[:user_login]).pluck(:id).first | |||
| is_exists = Journal.exists?(user_id: u_id, journalized_id: re[:target_params][:journalized_id], created_on: re[:target_params][:created_on].to_s.to_time) | |||
| if is_exists | |||
| SyncLog.sync_log("***00000. Journal:#{re[:target_params][:id]}-is exists--------------") | |||
| else | |||
| @@ -142,7 +140,7 @@ class SyncProjectsJob < ApplicationJob | |||
| end | |||
| else | |||
| SyncLog.sync_log("***111222. journal_create failed---------------") | |||
| end | |||
| end | |||
| end | |||
| @@ -181,4 +179,4 @@ class SyncProjectsJob < ApplicationJob | |||
| SyncLog.sync_log("***111222. end_to_create_target---------------") | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,31 @@ | |||
| module Ci | |||
| class Database < ActiveRecord::Base | |||
| self.abstract_class = true | |||
| # Dynamically sets the database connection. | |||
| def self.set_connection(params) | |||
| puts "[Ci::Database] set db connection params: #{params}" | |||
| establish_connection( | |||
| adapter: params[:adapter], | |||
| database: params[:database], | |||
| port: params[:port].to_i, | |||
| host: params[:host], | |||
| username: params[:username], | |||
| password: params[:password], | |||
| encoding: "utf8" | |||
| ) | |||
| end | |||
| def self.get_connection_params(connect_to) | |||
| params = Hash.new | |||
| params[:adapter] = "mysql2" | |||
| params[:host] = connect_to[:host].to_s | |||
| params[:username] = connect_to[:username].to_s | |||
| params[:password] = connect_to[:password].to_s | |||
| params[:database] = connect_to[:database].to_s | |||
| params[:port] = connect_to[:port] || "43306" | |||
| params[:encoding] = "utf8" | |||
| return params | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,81 @@ | |||
| class Ci::Drone::API < Ci::Drone::Request | |||
| attr_reader :drone_token, :endpoint, :owner, :repo, :options | |||
| # drone_token: | |||
| # owner: repo's owner name or login | |||
| # repo: repo's identifier | |||
| def initialize(drone_token, endpoint, owner, repo, options={}) | |||
| @drone_token = drone_token | |||
| @endpoint = endpoint | |||
| @owner = owner | |||
| @repo = repo | |||
| @options = options | |||
| end | |||
| # Build List | |||
| # GET api/repos/{owner}/{name}/builds | |||
| # eq: | |||
| # Ci::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, @repo.user.login, @repo.identifier) | |||
| def builds | |||
| get(endpoint, "api/repos/#{owner}/#{repo}/builds", drone_token: drone_token) | |||
| end | |||
| # Build Info | |||
| # GET api/repos/{owner}/{name}/builds/{number} | |||
| # eq: | |||
| # Ci::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, project.owner.login, project.identifier, number: number).build | |||
| def build | |||
| get(endpoint, "api/repos/#{owner}/#{repo}/builds/#{options[:number]}", drone_token: drone_token) | |||
| end | |||
| # Update .trustie-pipeline.yml file | |||
| # PATCH api/repos/{owner}/{name}\ | |||
| # eq: | |||
| # Ci::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, project.owner.login, project.identifier, config_path: config_path).config_yml | |||
| def config_yml | |||
| patch(endpoint, "/api/repos/#{owner}/#{repo}", drone_token: drone_token, config_path: options[:config_path]) | |||
| end | |||
| # Activate user's project with Drone CI | |||
| # POST api/repos/{owner}/{name} | |||
| # eq: | |||
| # Ci::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, project.owner.login, project.identifier).activate | |||
| def activate | |||
| post(endpoint, "/api/repos/#{owner}/#{repo}", drone_token: drone_token) | |||
| end | |||
| # Build Restart | |||
| # POST api/repos/{owner}/{name}/builds/{number} | |||
| # Restart the specified build. Please note this api requires read and write access to the repository and the request parameter {build} is not the build id but the build number. | |||
| # eq: | |||
| # Ci::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, @repo.user.login, @repo.identifier, number: number).restart | |||
| def restart | |||
| post(endpoint, "/api/repos/#{owner}/#{repo}/builds/#{options[:number]}", drone_token: drone_token) | |||
| end | |||
| # Build Stop | |||
| # DELETE api/repos/{owner}/{name}/builds/{number} | |||
| # Stop the specified build. Please note this api requires administrative privileges and the request parameter {build} is not the build id but the build number. | |||
| # eq: | |||
| # Ci::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, @repo.user.login, @repo.identifier, number: number).stop | |||
| def stop | |||
| delete(endpoint, "/api/repos/#{owner}/#{repo}/builds/#{options[:number]}", drone_token: drone_token) | |||
| end | |||
| # Build Logs | |||
| # GET /api/repos/{owner}/{repo}/builds/{build}/logs/{stage}/{step} | |||
| # Please note this api requires read access to the repository. | |||
| # eq: | |||
| # Ci::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, @repo.user.login, @repo.identifier, build: build, stage: stage, step: step).logs | |||
| def logs | |||
| get(endpoint, "/api/repos/#{owner}/#{repo}/builds/#{options[:build]}/logs/#{options[:stage]}/#{options[:step]}", drone_token: drone_token) | |||
| end | |||
| # Synchronize the currently authenticated user’s repository list. | |||
| # POST /api/user/repos | |||
| # eq: | |||
| # Ci::Drone::API.new(drone_token, cloud_account.drone_url, @repo.user.login, @repo.identifier, number: number).sync | |||
| def sync_repos | |||
| post(endpoint, "/api/users/repos", drone_token: drone_token) | |||
| end | |||
| end | |||
| @@ -0,0 +1,25 @@ | |||
| class Ci::Drone::Ci | |||
| attr_reader :host, :username, :password, :gitea_username | |||
| # host: drone server's ip | |||
| # username: drone server's account | |||
| # password: drone server's password | |||
| # eq: | |||
| # DevOps::Drone::Ci.new(@cloud_account.drone_ip, @cloud_account.account, @cloud_account.visible_secret, current_user.login).get_token | |||
| def initialize(host, username, password, gitea_username) | |||
| @host = host | |||
| @username = username | |||
| @password = password | |||
| @gitea_username = gitea_username | |||
| end | |||
| def get_token | |||
| puts "--------- sshpass -p #{password} ssh -o 'StrictHostKeyChecking no' #{username}@#{host} '#{cmd}'" | |||
| `sshpass -p #{password} ssh -o "StrictHostKeyChecking no" #{username}@#{host} "#{cmd}"` | |||
| end | |||
| private | |||
| def cmd | |||
| "cd ..; cd var/lib/drone/; sqlite3 database.sqlite; .dump; select user_hash from users where user_login=#{gitea_username};" | |||
| end | |||
| end | |||
| @@ -0,0 +1,25 @@ | |||
| class Ci::Drone::Client | |||
| attr_reader :client_id, :drone_ip, :rpc_secret | |||
| # client_id: user's client_id from oauth | |||
| # drone_ip: 云服务器IP地址, eq: 173.65.32.21 | |||
| # eq: | |||
| # DevOps::Drone::Client.new(current_user.oauth.client_id, 'drone_ip').generate_cmd | |||
| def initialize(client_id, drone_ip, rpc_secret) | |||
| @client_id = client_id | |||
| @drone_ip = drone_ip | |||
| @rpc_secret = rpc_secret | |||
| end | |||
| def generate_cmd | |||
| "docker run -d \ | |||
| -v /var/run/docker.sock:/var/run/docker.sock \ | |||
| -e DRONE_RPC_HOST=#{drone_ip}:80 \ | |||
| -e DRONE_RPC_SECRET=#{rpc_secret} \ | |||
| -e DRONE_RUNNER_NAME=#{drone_ip} \ | |||
| --restart always \ | |||
| --name drone-agent--#{client_id} \ | |||
| --net='bridge' \ | |||
| drone/drone-runner-docker:1" | |||
| end | |||
| end | |||
| @@ -0,0 +1,14 @@ | |||
| class Ci::Drone::Error < StandardError | |||
| attr_reader :code | |||
| def initialize(code, message) | |||
| super message | |||
| @code = code | |||
| end | |||
| class << self | |||
| def parse(result) | |||
| new(result['errcode'], result['errmsg']) | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,108 @@ | |||
| class Ci::Drone::Request | |||
| # Converts the response body to an ObjectifiedHash. | |||
| def self.parse(body) | |||
| body = decode(body) | |||
| if body.is_a? Hash | |||
| ObjectifiedHash.new body | |||
| elsif body.is_a? Array | |||
| body.collect! { |e| ObjectifiedHash.new(e) } | |||
| elsif body == true | |||
| body | |||
| else | |||
| raise Error::Parsing.new "Couldn't parse a response body" | |||
| end | |||
| end | |||
| # Decodes a JSON response into Ruby object. | |||
| def self.decode(response) | |||
| begin | |||
| JSON.load response | |||
| rescue JSON::ParserError | |||
| raise Error::Parsing.new "The response is not a valid JSON" | |||
| end | |||
| end | |||
| def get(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:get, endpoint, path, options) | |||
| end | |||
| def post(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:post, endpoint, path, options) | |||
| end | |||
| def put(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:put, endpoint, path, options) | |||
| end | |||
| def patch(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:patch, endpoint, path, options) | |||
| end | |||
| def delete(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:delete, endpoint, path, options) | |||
| end | |||
| private | |||
| def request(method, endpoint, path, **params) | |||
| Rails.logger.info("[drone] request: #{method} #{path} #{params.except(:drone_token).inspect}") | |||
| client ||= begin | |||
| Faraday.new(url: endpoint) do |req| | |||
| req.request :url_encoded | |||
| req.headers['Content-Type'] = 'application/json' | |||
| req.response :logger # 显示日志 | |||
| req.adapter Faraday.default_adapter | |||
| req.authorization :Bearer, params[:drone_token] | |||
| req.headers['Authorization'] | |||
| end | |||
| end | |||
| response = client.public_send(method, path) do |req| | |||
| req.body = params.except(:drone_token).to_json | |||
| end | |||
| json_response(response) | |||
| end | |||
| # Checks the response code for common errors. | |||
| # Returns parsed response for successful requests. | |||
| def validate(response) | |||
| # case response.code | |||
| # when 400; raise Error::BadRequest.new error_message(response) | |||
| # when 401; raise Error::Unauthorized.new error_message(response) | |||
| # when 403; raise Error::Forbidden.new error_message(response) | |||
| # when 404; raise Error::NotFound.new error_message(response) | |||
| # when 405; raise Error::MethodNotAllowed.new error_message(response) | |||
| # when 406; raise Error::DataNotAccepted.new error_message(response) | |||
| # when 409; raise Error::Conflict.new error_message(response) | |||
| # when 500; raise Error::InternalServerError.new error_message(response) | |||
| # when 502; raise Error::BadGateway.new error_message(response) | |||
| # when 503; raise Error::ServiceUnavailable.new error_message(response) | |||
| # end | |||
| response.parsed_response | |||
| end | |||
| # Checks a base_uri and params for requests. | |||
| def validate_request_params!(endpoint) | |||
| raise "Please set an endpoint to API" unless endpoint | |||
| end | |||
| def error_message(response) | |||
| "Server responded with code #{response.code}, message: #{response.parsed_response.message}. " \ | |||
| "Request URI: #{response.request.base_uri}#{response.request.path}" | |||
| end | |||
| def json_response(response) | |||
| result = JSON.parse(response.body) | |||
| status = response.status | |||
| Rails.logger.info("[drone] response:#{status} #{result.inspect}") | |||
| response.status != 200 ? result.merge!(status: response.status) : result | |||
| end | |||
| end | |||
| @@ -0,0 +1,62 @@ | |||
| class Ci::Drone::Server | |||
| attr_reader :user_login, :client_id, :client_secret, :drone_host, :rpc_secret | |||
| # client_id: user's client_id from oauth | |||
| # client_secret: user's client_id from oauth | |||
| # drone_host: 云服务器地址,eq: 173.53.21.31:80 | |||
| # eg: | |||
| # DevOps::Drone::Server.new(current_user.login, current_user.oauth.client_id, current_user.oauth.client_secret, 'drone_host').generate_cmd | |||
| def initialize(user_login, client_id, client_secret, drone_host, rpc_secret) | |||
| @user_login = user_login | |||
| @client_id = client_id | |||
| @drone_host = drone_host | |||
| @rpc_secret = rpc_secret | |||
| @client_secret = client_secret | |||
| end | |||
| # TODO 一下代码方便测试,正式环境请移除 | |||
| # docker rm -f `docker ps -qa` | |||
| def generate_cmd | |||
| "service docker start; docker run \ | |||
| -v /var/run/docker.sock:/var/run/docker.sock \ | |||
| -e DRONE_DATABASE_DRIVER=mysql \ | |||
| -e DRONE_DATABASE_DATASOURCE=#{database_username}:'#{database_password}'@tcp\\(#{database_host}:#{database_port}\\)/#{user_login}_drone?parseTime=true \ | |||
| -e DRONE_GITEA_SERVER=#{gitea_url} \ | |||
| -e DRONE_GITEA_CLIENT_ID=#{client_id} \ | |||
| -e DRONE_GITEA_CLIENT_SECRET=#{client_secret} \ | |||
| -e DRONE_RPC_SECRET=#{rpc_secret} \ | |||
| -e DRONE_SERVER_HOST=#{drone_host} \ | |||
| -e DRONE_SERVER_PROTO=http \ | |||
| -p '80:80' \ | |||
| --restart=always \ | |||
| --detach=true \ | |||
| --name=drone-server-#{client_id} \ | |||
| --net='bridge' \ | |||
| drone/drone:1" | |||
| end | |||
| private | |||
| def gitea_url | |||
| Gitea.gitea_config[:domain] | |||
| end | |||
| def database_username | |||
| database_config[Rails.env]["ci_server_db"]["username"] | |||
| end | |||
| def database_password | |||
| database_config[Rails.env]["ci_server_db"]["password"] | |||
| end | |||
| def database_host | |||
| database_config[Rails.env]["ci_server_db"]["host"] | |||
| end | |||
| def database_port | |||
| database_config[Rails.env]["ci_server_db"]["port"] || 3306 | |||
| end | |||
| def database_config | |||
| Rails.configuration.database_configuration | |||
| end | |||
| end | |||
| @@ -0,0 +1,22 @@ | |||
| class Ci::Drone::Start | |||
| attr_reader :drone_username, :drone_password, :drone_host, :drone_server_cmd, :drone_client_cmd | |||
| # drone_username="XXXX" 云服务器登录用户名 | |||
| # drone_password="XXXXX" 云服务器用户密码 | |||
| # drone_host="" 云服务器地址 | |||
| # eq: | |||
| # drone_server_cmd = DevOps::Drone::Server.new('client_id', 'client_secret', 'drone_url').generate_cmd | |||
| # drone_client_cmd = DevOps::Drone::Client.new('client_id', 'server_url').generate_cmd | |||
| # DevOps::Drone::Start.new(drone_username, drone_password, 'drone_host', drone_server_cmd, drone_client_cmd).run | |||
| def initialize(drone_username, drone_password, drone_host, drone_server_cmd, drone_client_cmd) | |||
| @drone_username = drone_username | |||
| @drone_password = drone_password | |||
| @drone_host = drone_host | |||
| @drone_server_cmd = drone_server_cmd | |||
| @drone_client_cmd = drone_client_cmd | |||
| end | |||
| def run | |||
| `sshpass -p #{drone_password} ssh -o "StrictHostKeyChecking no" #{drone_username}@#{drone_host} "#{drone_server_cmd} && #{drone_client_cmd}"` | |||
| end | |||
| end | |||
| @@ -0,0 +1,260 @@ | |||
| module Ci::Schema | |||
| class << self | |||
| def statement | |||
| sqls = <<-SQL | |||
| CREATE TABLE IF NOT EXISTS `repos` ( | |||
| `repo_id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `repo_uid` varchar(250) DEFAULT NULL, | |||
| `repo_user_id` int(11) DEFAULT NULL, | |||
| `repo_namespace` varchar(250) DEFAULT NULL, | |||
| `repo_name` varchar(250) DEFAULT NULL, | |||
| `repo_slug` varchar(250) DEFAULT NULL, | |||
| `repo_scm` varchar(50) DEFAULT NULL, | |||
| `repo_clone_url` varchar(2000) DEFAULT NULL, | |||
| `repo_ssh_url` varchar(2000) DEFAULT NULL, | |||
| `repo_html_url` varchar(2000) DEFAULT NULL, | |||
| `repo_active` tinyint(1) DEFAULT NULL, | |||
| `repo_private` tinyint(1) DEFAULT NULL, | |||
| `repo_visibility` varchar(50) DEFAULT NULL, | |||
| `repo_branch` varchar(250) DEFAULT NULL, | |||
| `repo_counter` int(11) DEFAULT NULL, | |||
| `repo_config` varchar(500) DEFAULT NULL, | |||
| `repo_timeout` int(11) DEFAULT NULL, | |||
| `repo_trusted` tinyint(1) DEFAULT NULL, | |||
| `repo_protected` tinyint(1) DEFAULT NULL, | |||
| `repo_synced` int(11) DEFAULT NULL, | |||
| `repo_created` int(11) DEFAULT NULL, | |||
| `repo_updated` int(11) DEFAULT NULL, | |||
| `repo_version` int(11) DEFAULT NULL, | |||
| `repo_signer` varchar(50) DEFAULT NULL, | |||
| `repo_secret` varchar(50) DEFAULT NULL, | |||
| PRIMARY KEY (`repo_id`), | |||
| UNIQUE KEY `repo_slug` (`repo_slug`), | |||
| UNIQUE KEY `repo_uid` (`repo_uid`) | |||
| ) ENGINE=InnoDB AUTO_INCREMENT=137 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `builds` ( | |||
| `build_id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `build_repo_id` int(11) DEFAULT NULL, | |||
| `build_config_id` int(11) DEFAULT NULL, | |||
| `build_trigger` varchar(250) DEFAULT NULL, | |||
| `build_number` int(11) DEFAULT NULL, | |||
| `build_parent` int(11) DEFAULT NULL, | |||
| `build_status` varchar(50) DEFAULT NULL, | |||
| `build_error` varchar(500) DEFAULT NULL, | |||
| `build_event` varchar(50) DEFAULT NULL, | |||
| `build_action` varchar(50) DEFAULT NULL, | |||
| `build_link` varchar(1000) DEFAULT NULL, | |||
| `build_timestamp` int(11) DEFAULT NULL, | |||
| `build_title` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, | |||
| `build_message` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, | |||
| `build_before` varchar(50) DEFAULT NULL, | |||
| `build_after` varchar(50) DEFAULT NULL, | |||
| `build_ref` varchar(500) DEFAULT NULL, | |||
| `build_source_repo` varchar(250) DEFAULT NULL, | |||
| `build_source` varchar(500) DEFAULT NULL, | |||
| `build_target` varchar(500) DEFAULT NULL, | |||
| `build_author` varchar(500) DEFAULT NULL, | |||
| `build_author_name` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, | |||
| `build_author_email` varchar(500) DEFAULT NULL, | |||
| `build_author_avatar` varchar(1000) DEFAULT NULL, | |||
| `build_sender` varchar(500) DEFAULT NULL, | |||
| `build_deploy` varchar(500) DEFAULT NULL, | |||
| `build_params` varchar(2000) DEFAULT NULL, | |||
| `build_started` int(11) DEFAULT NULL, | |||
| `build_finished` int(11) DEFAULT NULL, | |||
| `build_created` int(11) DEFAULT NULL, | |||
| `build_updated` int(11) DEFAULT NULL, | |||
| `build_version` int(11) DEFAULT NULL, | |||
| PRIMARY KEY (`build_id`), | |||
| UNIQUE KEY `build_repo_id` (`build_repo_id`,`build_number`) | |||
| ) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `cron` ( | |||
| `cron_id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `cron_repo_id` int(11) DEFAULT NULL, | |||
| `cron_name` varchar(50) DEFAULT NULL, | |||
| `cron_expr` varchar(50) DEFAULT NULL, | |||
| `cron_next` int(11) DEFAULT NULL, | |||
| `cron_prev` int(11) DEFAULT NULL, | |||
| `cron_event` varchar(50) DEFAULT NULL, | |||
| `cron_branch` varchar(250) DEFAULT NULL, | |||
| `cron_target` varchar(250) DEFAULT NULL, | |||
| `cron_disabled` tinyint(1) DEFAULT NULL, | |||
| `cron_created` int(11) DEFAULT NULL, | |||
| `cron_updated` int(11) DEFAULT NULL, | |||
| `cron_version` int(11) DEFAULT NULL, | |||
| PRIMARY KEY (`cron_id`), | |||
| UNIQUE KEY `cron_repo_id` (`cron_repo_id`,`cron_name`), | |||
| CONSTRAINT `cron_ibfk_1` FOREIGN KEY (`cron_repo_id`) REFERENCES `repos` (`repo_id`) ON DELETE CASCADE | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |||
| CREATE TABLE IF NOT EXISTS `latest` ( | |||
| `latest_repo_id` int(11) NOT NULL DEFAULT '0', | |||
| `latest_build_id` int(11) DEFAULT NULL, | |||
| `latest_type` varchar(50) NOT NULL DEFAULT '', | |||
| `latest_name` varchar(500) NOT NULL DEFAULT '', | |||
| `latest_created` int(11) DEFAULT NULL, | |||
| `latest_updated` int(11) DEFAULT NULL, | |||
| `latest_deleted` int(11) DEFAULT NULL, | |||
| PRIMARY KEY (`latest_repo_id`,`latest_type`,`latest_name`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `logs` ( | |||
| `log_id` int(11) NOT NULL, | |||
| `log_data` mediumblob, | |||
| PRIMARY KEY (`log_id`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `migrations` ( | |||
| `name` varchar(255) DEFAULT NULL, | |||
| UNIQUE KEY `name` (`name`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |||
| CREATE TABLE IF NOT EXISTS `nodes` ( | |||
| `node_id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `node_uid` varchar(500) DEFAULT NULL, | |||
| `node_provider` varchar(50) DEFAULT NULL, | |||
| `node_state` varchar(50) DEFAULT NULL, | |||
| `node_name` varchar(50) DEFAULT NULL, | |||
| `node_image` varchar(500) DEFAULT NULL, | |||
| `node_region` varchar(100) DEFAULT NULL, | |||
| `node_size` varchar(100) DEFAULT NULL, | |||
| `node_os` varchar(50) DEFAULT NULL, | |||
| `node_arch` varchar(50) DEFAULT NULL, | |||
| `node_kernel` varchar(50) DEFAULT NULL, | |||
| `node_variant` varchar(50) DEFAULT NULL, | |||
| `node_address` varchar(500) DEFAULT NULL, | |||
| `node_capacity` int(11) DEFAULT NULL, | |||
| `node_filter` varchar(2000) DEFAULT NULL, | |||
| `node_labels` varchar(2000) DEFAULT NULL, | |||
| `node_error` varchar(2000) DEFAULT NULL, | |||
| `node_ca_key` blob, | |||
| `node_ca_cert` blob, | |||
| `node_tls_key` blob, | |||
| `node_tls_cert` blob, | |||
| `node_tls_name` varchar(500) DEFAULT NULL, | |||
| `node_paused` tinyint(1) DEFAULT NULL, | |||
| `node_protected` tinyint(1) DEFAULT NULL, | |||
| `node_created` int(11) DEFAULT NULL, | |||
| `node_updated` int(11) DEFAULT NULL, | |||
| `node_pulled` int(11) DEFAULT NULL, | |||
| PRIMARY KEY (`node_id`), | |||
| UNIQUE KEY `node_name` (`node_name`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `orgsecrets` ( | |||
| `secret_id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `secret_namespace` varchar(50) DEFAULT NULL, | |||
| `secret_name` varchar(200) DEFAULT NULL, | |||
| `secret_type` varchar(50) DEFAULT NULL, | |||
| `secret_data` blob, | |||
| `secret_pull_request` tinyint(1) DEFAULT NULL, | |||
| `secret_pull_request_push` tinyint(1) DEFAULT NULL, | |||
| PRIMARY KEY (`secret_id`), | |||
| UNIQUE KEY `secret_namespace` (`secret_namespace`,`secret_name`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `perms` ( | |||
| `perm_user_id` int(11) NOT NULL DEFAULT '0', | |||
| `perm_repo_uid` varchar(250) NOT NULL DEFAULT '', | |||
| `perm_read` tinyint(1) DEFAULT NULL, | |||
| `perm_write` tinyint(1) DEFAULT NULL, | |||
| `perm_admin` tinyint(1) DEFAULT NULL, | |||
| `perm_synced` int(11) DEFAULT NULL, | |||
| `perm_created` int(11) DEFAULT NULL, | |||
| `perm_updated` int(11) DEFAULT NULL, | |||
| PRIMARY KEY (`perm_user_id`,`perm_repo_uid`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `secrets` ( | |||
| `secret_id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `secret_repo_id` int(11) DEFAULT NULL, | |||
| `secret_name` varchar(500) DEFAULT NULL, | |||
| `secret_data` blob, | |||
| `secret_pull_request` tinyint(1) DEFAULT NULL, | |||
| `secret_pull_request_push` tinyint(1) DEFAULT NULL, | |||
| PRIMARY KEY (`secret_id`), | |||
| UNIQUE KEY `secret_repo_id` (`secret_repo_id`,`secret_name`), | |||
| CONSTRAINT `secrets_ibfk_1` FOREIGN KEY (`secret_repo_id`) REFERENCES `repos` (`repo_id`) ON DELETE CASCADE | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `stages` ( | |||
| `stage_id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `stage_repo_id` int(11) DEFAULT NULL, | |||
| `stage_build_id` int(11) DEFAULT NULL, | |||
| `stage_number` int(11) DEFAULT NULL, | |||
| `stage_name` varchar(100) DEFAULT NULL, | |||
| `stage_kind` varchar(50) DEFAULT NULL, | |||
| `stage_type` varchar(50) DEFAULT NULL, | |||
| `stage_status` varchar(50) DEFAULT NULL, | |||
| `stage_error` varchar(500) DEFAULT NULL, | |||
| `stage_errignore` tinyint(1) DEFAULT NULL, | |||
| `stage_exit_code` int(11) DEFAULT NULL, | |||
| `stage_limit` int(11) DEFAULT NULL, | |||
| `stage_os` varchar(50) DEFAULT NULL, | |||
| `stage_arch` varchar(50) DEFAULT NULL, | |||
| `stage_variant` varchar(10) DEFAULT NULL, | |||
| `stage_kernel` varchar(50) DEFAULT NULL, | |||
| `stage_machine` varchar(500) DEFAULT NULL, | |||
| `stage_started` int(11) DEFAULT NULL, | |||
| `stage_stopped` int(11) DEFAULT NULL, | |||
| `stage_created` int(11) DEFAULT NULL, | |||
| `stage_updated` int(11) DEFAULT NULL, | |||
| `stage_version` int(11) DEFAULT NULL, | |||
| `stage_on_success` tinyint(1) DEFAULT NULL, | |||
| `stage_on_failure` tinyint(1) DEFAULT NULL, | |||
| `stage_depends_on` text, | |||
| `stage_labels` text, | |||
| PRIMARY KEY (`stage_id`), | |||
| UNIQUE KEY `stage_build_id` (`stage_build_id`,`stage_number`) | |||
| ) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `stages_unfinished` ( | |||
| `stage_id` int(11) NOT NULL, | |||
| PRIMARY KEY (`stage_id`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `steps` ( | |||
| `step_id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `step_stage_id` int(11) DEFAULT NULL, | |||
| `step_number` int(11) DEFAULT NULL, | |||
| `step_name` varchar(100) DEFAULT NULL, | |||
| `step_status` varchar(50) DEFAULT NULL, | |||
| `step_error` varchar(500) DEFAULT NULL, | |||
| `step_errignore` tinyint(1) DEFAULT NULL, | |||
| `step_exit_code` int(11) DEFAULT NULL, | |||
| `step_started` int(11) DEFAULT NULL, | |||
| `step_stopped` int(11) DEFAULT NULL, | |||
| `step_version` int(11) DEFAULT NULL, | |||
| PRIMARY KEY (`step_id`), | |||
| UNIQUE KEY `step_stage_id` (`step_stage_id`,`step_number`) | |||
| ) ENGINE=InnoDB AUTO_INCREMENT=83 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| CREATE TABLE IF NOT EXISTS `users` ( | |||
| `user_id` int(11) NOT NULL AUTO_INCREMENT, | |||
| `user_login` varchar(250) DEFAULT NULL, | |||
| `user_email` varchar(500) DEFAULT NULL, | |||
| `user_admin` tinyint(1) DEFAULT NULL, | |||
| `user_machine` tinyint(1) DEFAULT NULL, | |||
| `user_active` tinyint(1) DEFAULT NULL, | |||
| `user_avatar` varchar(2000) DEFAULT NULL, | |||
| `user_syncing` tinyint(1) DEFAULT NULL, | |||
| `user_synced` int(11) DEFAULT NULL, | |||
| `user_created` int(11) DEFAULT NULL, | |||
| `user_updated` int(11) DEFAULT NULL, | |||
| `user_last_login` int(11) DEFAULT NULL, | |||
| `user_oauth_token` varchar(500) DEFAULT NULL, | |||
| `user_oauth_refresh` varchar(500) DEFAULT NULL, | |||
| `user_oauth_expiry` int(11) DEFAULT NULL, | |||
| `user_hash` varchar(500) DEFAULT NULL, | |||
| PRIMARY KEY (`user_id`), | |||
| UNIQUE KEY `user_login` (`user_login`), | |||
| UNIQUE KEY `user_hash` (`user_hash`) | |||
| ) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | |||
| SQL | |||
| sqls | |||
| end | |||
| end | |||
| end | |||
| @@ -5,4 +5,5 @@ module CustomRegexp | |||
| NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/ | |||
| PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/ | |||
| URL = /\Ahttps?:\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]\z/ | |||
| end | |||
| IP = /^((\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/ | |||
| end | |||
| @@ -0,0 +1,12 @@ | |||
| module Gitea | |||
| class Database < ActiveRecord::Base | |||
| self.abstract_class = true | |||
| def self.set_connection | |||
| gitea_server_config = Rails.configuration.database_configuration[Rails.env]["gitea_server"] | |||
| raise 'gitea database config missing' if gitea_server_config.blank? | |||
| establish_connection gitea_server_config | |||
| end | |||
| end | |||
| end | |||
| @@ -1,5 +1,23 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: applied_messages | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer | |||
| # applied_id :integer | |||
| # applied_type :string(255) | |||
| # viewed :integer default("0") | |||
| # status :integer default("0") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # name :string(255) | |||
| # applied_user_id :integer | |||
| # role :integer | |||
| # project_id :integer | |||
| # | |||
| class AppliedMessage < ApplicationRecord | |||
| belongs_to :user | |||
| belongs_to :applied, polymorphic: true | |||
| end | |||
| end | |||
| @@ -1,3 +1,14 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: applied_projects | |||
| # | |||
| # id :integer not null, primary key | |||
| # project_id :integer not null | |||
| # user_id :integer not null | |||
| # role :integer default("0") | |||
| # status :integer default("0") | |||
| # | |||
| class AppliedProject < ApplicationRecord | |||
| belongs_to :user | |||
| belongs_to :project | |||
| @@ -1,3 +1,26 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: apply_actions | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer | |||
| # reason :string(255) | |||
| # container_id :integer | |||
| # container_type :string(255) | |||
| # dealer_id :integer | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # status :integer default("0") | |||
| # apply_reason :text(65535) | |||
| # noticed :boolean default("0") | |||
| # ip_addr :string(255) | |||
| # reject_description :string(255) | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_apply_actions_on_user_id (user_id) | |||
| # | |||
| # 申请消息 | |||
| class ApplyAction < ApplicationRecord | |||
| belongs_to :user | |||
| @@ -27,4 +50,4 @@ class ApplyAction < ApplicationRecord | |||
| belong_container_id: container_id, belong_container_type: belong_container_type) | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -1,3 +1,23 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: apply_user_authentications | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer | |||
| # status :integer | |||
| # auth_type :integer | |||
| # remarks :string(255) | |||
| # dealer :integer | |||
| # deal_time :datetime | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # is_delete :boolean default("0") | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_apply_user_authentications_on_user_id (user_id) | |||
| # | |||
| # status:0 审核中 1 同意 2 拒绝 3 撤销 | |||
| # auth_type:1 实名认证, 2 职业认证 | |||
| class ApplyUserAuthentication < ApplicationRecord | |||
| @@ -1,3 +1,42 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: attachments | |||
| # | |||
| # id :integer not null, primary key | |||
| # container_id :integer | |||
| # container_type :string(30) | |||
| # filename :string(255) default(""), not null | |||
| # disk_filename :string(255) default(""), not null | |||
| # filesize :integer default("0"), not null | |||
| # content_type :string(255) default("") | |||
| # digest :string(60) default(""), not null | |||
| # downloads :integer default("0"), not null | |||
| # author_id :integer default("0"), not null | |||
| # created_on :datetime | |||
| # description :text(65535) | |||
| # disk_directory :string(255) | |||
| # attachtype :integer default("1") | |||
| # is_public :integer default("1") | |||
| # copy_from :integer | |||
| # quotes :integer default("0") | |||
| # is_publish :integer default("1") | |||
| # publish_time :datetime | |||
| # resource_bank_id :integer | |||
| # unified_setting :boolean default("1") | |||
| # cloud_url :string(255) default("") | |||
| # course_second_category_id :integer default("0") | |||
| # delay_publish :boolean default("0") | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_attachments_on_author_id (author_id) | |||
| # index_attachments_on_container_id_and_container_type (container_id,container_type) | |||
| # index_attachments_on_course_second_category_id (course_second_category_id) | |||
| # index_attachments_on_created_on (created_on) | |||
| # index_attachments_on_is_public (is_public) | |||
| # index_attachments_on_quotes (quotes) | |||
| # | |||
| class Attachment < ApplicationRecord | |||
| include BaseModel | |||
| include Publicable | |||
| @@ -1,3 +1,22 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: attachment_group_settings | |||
| # | |||
| # id :integer not null, primary key | |||
| # attachment_id :integer | |||
| # course_group_id :integer | |||
| # course_id :integer | |||
| # publish_time :datetime | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_attachment_group_settings_on_attachment_id (attachment_id) | |||
| # index_attachment_group_settings_on_course_group_id (course_group_id) | |||
| # index_attachment_group_settings_on_course_id (course_id) | |||
| # | |||
| class AttachmentGroupSetting < ActiveRecord::Base | |||
| belongs_to :attachment | |||
| # belongs_to :course_group | |||
| @@ -1,3 +1,31 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: attachment_histories | |||
| # | |||
| # id :integer not null, primary key | |||
| # container_id :integer | |||
| # container_type :string(255) | |||
| # filename :string(255) default("") | |||
| # disk_filename :string(255) default("") | |||
| # filesize :integer default("0") | |||
| # content_type :string(255) default("") | |||
| # digest :string(60) default("") | |||
| # downloads :integer default("0") | |||
| # author_id :integer | |||
| # created_on :datetime | |||
| # description :text(65535) | |||
| # disk_directory :string(255) | |||
| # attachtype :integer | |||
| # is_public :integer | |||
| # copy_from :integer | |||
| # quotes :integer | |||
| # version :integer | |||
| # attachment_id :integer | |||
| # is_publish :integer default("1") | |||
| # publish_time :date | |||
| # cloud_url :string(255) default("") | |||
| # | |||
| class AttachmentHistory < ApplicationRecord | |||
| include Publishable | |||
| include Publicable | |||
| @@ -1,3 +1,20 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: bidding_users | |||
| # | |||
| # id :integer not null, primary key | |||
| # project_package_id :integer | |||
| # user_id :integer | |||
| # status :string(255) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_bidding_users_on_project_package_id (project_package_id) | |||
| # index_bidding_users_on_user_id (user_id) | |||
| # | |||
| class BiddingUser < ApplicationRecord | |||
| include AASM | |||
| @@ -21,4 +38,4 @@ class BiddingUser < ApplicationRecord | |||
| def status_text | |||
| I18n.t("bidding_user.status.#{status}") | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,5 @@ | |||
| module Ci | |||
| # def self.table_name_prefix | |||
| # 'ci_' | |||
| # end | |||
| end | |||
| @@ -0,0 +1,14 @@ | |||
| class Ci::Build < Ci::RemoteBase | |||
| self.primary_key = 'build_id' | |||
| belongs_to :repo, foreign_key: :build_repo_id | |||
| has_many :stages, foreign_key: "stage_build_id", dependent: :destroy | |||
| scope :successed, ->{ by_status('success') } | |||
| scope :failed, -> { by_status('failure') } | |||
| scope :running, -> { by_status('running') } | |||
| scope :errored, -> { by_status('error') } | |||
| scope :pending, -> { by_status('pending') } | |||
| scope :killed, -> { by_status('killed') } | |||
| scope :by_status, ->(status) { where(build_status: status) } | |||
| end | |||
| @@ -0,0 +1,47 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: ci_cloud_accounts | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer not null | |||
| # ip_num :integer | |||
| # account :string(255) | |||
| # secret :string(255) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # ci_user_id :integer | |||
| # | |||
| # Indexes | |||
| # | |||
| # dev_ops_cloud_accounts_p_u_ip (user_id,ip_num) | |||
| # | |||
| class Ci::CloudAccount < Ci::LocalBase | |||
| belongs_to :user | |||
| belongs_to :ci_user, class_name: 'Ci::User', foreign_key: :ci_user_id, optional: true | |||
| def drone_host | |||
| [drone_ip, ":80"].join | |||
| end | |||
| def drone_ip | |||
| IPAddr.new(self.ip_num, Socket::AF_INET).to_s | |||
| end | |||
| def drone_url | |||
| ["http://", self.drone_host].join | |||
| end | |||
| def visible_secret | |||
| Base64.decode64(secret) | |||
| end | |||
| def self.encrypted_secret(str) | |||
| Base64.encode64(str.strip).gsub(/\n/, '') | |||
| end | |||
| def authenticate_url | |||
| [drone_url, '/login'].join | |||
| end | |||
| end | |||
| @@ -0,0 +1,28 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: ci_languages | |||
| # | |||
| # id :integer not null, primary key | |||
| # name :string(255) not null | |||
| # content :text(65535) not null | |||
| # usage_amount :integer default("0") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # cover_id :integer | |||
| # | |||
| class Ci::Language < Ci::LocalBase | |||
| # before_save :encode_content | |||
| belongs_to :cover, class_name: "Attachment", foreign_key: :cover_id, optional: true | |||
| scope :six_common, -> { limit(6).by_usage_amount_desc } | |||
| scope :without_content, -> { select(column_names - ['content']) } | |||
| scope :by_usage_amount_desc, -> { order(usage_amount: :desc) } | |||
| private | |||
| def encode_content | |||
| self.content = Base64.encode64 content | |||
| end | |||
| end | |||
| @@ -0,0 +1,7 @@ | |||
| class Ci::LocalBase < ApplicationRecord | |||
| self.abstract_class = true | |||
| def self.table_name_prefix | |||
| "ci_" | |||
| end | |||
| end | |||
| @@ -0,0 +1,5 @@ | |||
| class Ci::Log < Ci::RemoteBase | |||
| self.primary_key = nil | |||
| belongs_to :step, class_name: 'Ci::Step', foreign_key: :log_id | |||
| end | |||
| @@ -0,0 +1,20 @@ | |||
| class Ci::Perm < Ci::RemoteBase | |||
| self.primary_key = nil | |||
| belongs_to :user, class_name: 'Ci::User', foreign_key: :perm_user_id | |||
| belongs_to :repo, class_name: 'Ci::Repo', foreign_key: :perm_repo_uid | |||
| def self.auto_create!(user, repo) | |||
| perm = new( | |||
| perm_user_id: user.user_id, | |||
| perm_repo_uid: repo.repo_id, | |||
| perm_read: true, | |||
| perm_write: true, | |||
| perm_admin: true, | |||
| perm_synced: 0, | |||
| perm_created: Time.now.to_i, | |||
| perm_updated: Time.now.to_i | |||
| ) | |||
| perm.save! | |||
| end | |||
| end | |||
| @@ -0,0 +1,8 @@ | |||
| class Ci::RemoteBase < Ci::Database | |||
| self.abstract_class = true | |||
| def generate_code | |||
| [*'a'..'z',*'0'..'9',*'A'..'Z'].sample(32).join | |||
| end | |||
| end | |||
| @@ -0,0 +1,64 @@ | |||
| class Ci::Repo < Ci::RemoteBase | |||
| self.primary_key = 'repo_id' | |||
| belongs_to :user, foreign_key: :repo_user_id | |||
| has_one :perm, foreign_key: :perm_repo_uid | |||
| has_many :builds, foreign_key: :build_repo_id, dependent: :destroy | |||
| def self.find_with_namespace(namespace_path, identifier) | |||
| logger.info "########namespace_path: #{namespace_path} ########identifier: #{identifier} " | |||
| user = Ci::User.find_by_user_login namespace_path | |||
| repo = Ci::Repo.where(repo_namespace: namespace_path, repo_name: identifier).first | |||
| [user, repo] | |||
| end | |||
| def activate!(ci_user_id) | |||
| update(repo_active: 1, | |||
| repo_signer: generate_code, | |||
| repo_secret: generate_code, | |||
| repo_user_id: ci_user_id, | |||
| repo_timeout: 60, | |||
| repo_config: '.trustie-pipeline.yml', | |||
| repo_updated: Time.now.to_i) | |||
| end | |||
| def self.auto_create!(user, project) | |||
| repo = new( | |||
| repo_user_id: user.user_id, | |||
| repo_namespace: project.owner.login, | |||
| repo_name: project.identifier, | |||
| repo_slug: "#{project.owner.login}/#{project.identifier}", | |||
| repo_scm: "git", | |||
| repo_ssh_url: "", | |||
| repo_html_url: "", | |||
| repo_clone_url: project.repository.url, | |||
| repo_active: 1, | |||
| repo_private: true, | |||
| repo_visibility: 'private', | |||
| repo_branch: 'master', | |||
| repo_counter: 0, | |||
| repo_trusted: false, | |||
| repo_protected: false, | |||
| repo_synced: 0, | |||
| repo_version: 1, | |||
| repo_timeout: 60, | |||
| repo_config: '.trustie-pipeline.yml', | |||
| repo_created: Time.now.to_i, | |||
| repo_updated: Time.now.to_i | |||
| ) | |||
| repo.repo_signer = repo.generate_code | |||
| repo.repo_secret = repo.generate_code | |||
| if repo.save! | |||
| Ci::Perm.auto_create!(user, repo) | |||
| repo.update_column(:repo_uid, repo.id) | |||
| repo | |||
| end | |||
| end | |||
| def deactivate! | |||
| update_column(:repo_active, 0) | |||
| end | |||
| end | |||
| @@ -0,0 +1,6 @@ | |||
| class Ci::Stage < Ci::RemoteBase | |||
| self.primary_key = 'stage_id' | |||
| belongs_to :build, foreign_key: :stage_build_id | |||
| has_many :steps, foreign_key: "step_stage_id", dependent: :destroy | |||
| end | |||
| @@ -0,0 +1,6 @@ | |||
| class Ci::Step < Ci::RemoteBase | |||
| self.primary_key = 'step_id' | |||
| belongs_to :stage, foreign_key: :step_stage_id | |||
| has_one :log, class_name: 'Ci::Log', foreign_key: :log_id | |||
| end | |||
| @@ -0,0 +1,68 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: users | |||
| # | |||
| # id :integer not null | |||
| # login :string(255) default(""), not null | |||
| # hashed_password :string(40) default(""), not null | |||
| # firstname :string(30) default(""), not null | |||
| # lastname :string(255) default(""), not null | |||
| # mail :string(60) | |||
| # admin :boolean default("0"), not null | |||
| # status :integer default("1"), not null | |||
| # last_login_on :datetime | |||
| # language :string(5) default("") | |||
| # auth_source_id :integer | |||
| # created_on :datetime | |||
| # updated_on :datetime | |||
| # type :string(255) | |||
| # identity_url :string(255) | |||
| # mail_notification :string(255) default(""), not null | |||
| # salt :string(64) | |||
| # gid :integer | |||
| # visits :integer default("0") | |||
| # excellent_teacher :integer default("0") | |||
| # excellent_student :integer default("0") | |||
| # phone :string(255) | |||
| # authentication :boolean default("0") | |||
| # grade :integer default("0") | |||
| # experience :integer default("0") | |||
| # nickname :string(255) | |||
| # show_realname :boolean default("1") | |||
| # professional_certification :boolean default("0") | |||
| # ID_number :string(255) | |||
| # certification :integer default("0") | |||
| # homepage_teacher :boolean default("0") | |||
| # homepage_engineer :boolean default("0") | |||
| # is_test :integer default("0") | |||
| # ecoder_user_id :integer default("0") | |||
| # business :boolean default("0") | |||
| # profile_completed :boolean default("0") | |||
| # laboratory_id :integer | |||
| # platform :string(255) default("0") | |||
| # gitea_token :string(255) | |||
| # gitea_uid :integer | |||
| # is_shixun_marker :boolean default("0") | |||
| # is_sync_pwd :boolean default("1") | |||
| # watchers_count :integer default("0") | |||
| # devops_step :integer default("0") | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_users_on_ecoder_user_id (ecoder_user_id) | |||
| # index_users_on_homepage_engineer (homepage_engineer) | |||
| # index_users_on_homepage_teacher (homepage_teacher) | |||
| # index_users_on_laboratory_id (laboratory_id) | |||
| # index_users_on_login (login) | |||
| # index_users_on_mail (mail) | |||
| # index_users_on_type (type) | |||
| # | |||
| class Ci::User < Ci::RemoteBase | |||
| self.primary_key = 'user_id' | |||
| has_many :repos, foreign_key: :repo_user_id, dependent: :destroy | |||
| has_many :perms, foreign_key: :perm_user_id, dependent: :delete_all | |||
| has_one :ci_cloud_account, class_name: 'Ci::CloudAccount', foreign_key: :ci_user_id | |||
| end | |||
| @@ -1,3 +1,23 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: composes | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer | |||
| # title :string(255) | |||
| # description :text(65535) | |||
| # show_mode :integer default("0") | |||
| # compose_mode :boolean default("0") | |||
| # compose_users_count :integer default("0") | |||
| # compose_projects_count :integer default("0") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_composes_on_user_id_and_show_mode_and_compose_mode (user_id,show_mode,compose_mode) | |||
| # | |||
| class Compose < ApplicationRecord | |||
| #组织 | |||
| belongs_to :user | |||
| @@ -1,3 +1,20 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: compose_projects | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer | |||
| # project_id :integer | |||
| # compose_id :integer | |||
| # position :integer default("0") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_compose_projects_on_user_id_and_project_id_and_compose_id (user_id,project_id,compose_id) | |||
| # | |||
| class ComposeProject < ApplicationRecord | |||
| #组织的项目记录表 | |||
| belongs_to :compose | |||
| @@ -1,3 +1,19 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: compose_users | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer | |||
| # compose_id :integer | |||
| # is_manager :integer default("0") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_compose_users_on_user_id_and_compose_id (user_id,compose_id) | |||
| # | |||
| class ComposeUser < ApplicationRecord | |||
| belongs_to :compose | |||
| belongs_to :user | |||
| @@ -0,0 +1,46 @@ | |||
| module Droneable | |||
| extend ActiveSupport::Concern | |||
| included do | |||
| end | |||
| def devops_uninit? | |||
| self.devops_step === User::DEVOPS_UNINIT | |||
| end | |||
| def devops_unverified? | |||
| self.devops_step === User::DEVOPS_UNVERIFIED | |||
| end | |||
| def devops_certification? | |||
| self.devops_step === User::DEVOPS_CERTIFICATION | |||
| end | |||
| def set_drone_step!(step) | |||
| self.update_column(:devops_step, step) | |||
| end | |||
| def ci_certification? | |||
| return false if self.is_a?(AnonymousUser) | |||
| devops_certification? && Ci::User.exists?(user_login: self.login) | |||
| end | |||
| def unbind_account! | |||
| user_projects = self.projects | |||
| user_projects.update_all(open_devops: false, open_devops_count: 0) | |||
| set_drone_step!(User::DEVOPS_UNINIT) | |||
| # TODO | |||
| # 删除用户项目下的与ci相关的所有webhook | |||
| user_projects.select(:id, :identifier, :gitea_webhook_id).each do |project| | |||
| if project.gitea_webhook_id | |||
| result = Gitea::Hooks::DestroyService.call(self.gitea_token, self.login, project.identifier, project.gitea_webhook_id) | |||
| project.update_column(:gitea_webhook_id, nil) if result.status == 204 | |||
| end | |||
| end | |||
| end | |||
| module ClassMethods | |||
| end | |||
| end | |||
| @@ -5,6 +5,7 @@ module Matchable | |||
| scope :with_project_category, ->(category_id) { where(project_category_id: category_id) unless category_id.blank? } | |||
| scope :with_project_language, ->(language_id) { where(project_language_id: language_id) unless language_id.blank? } | |||
| scope :with_project_type, ->(project_type) { where(project_type: project_type) if Project.project_types.include?(project_type) } | |||
| scope :by_name_or_identifier, ->(search) { where("name like :search or identifier LIKE :search", :search => "#{search.split(" ").join('|')}%") unless search.blank? } | |||
| end | |||
| end | |||
| @@ -7,6 +7,7 @@ module ProjectOperable | |||
| has_many :managers, -> { joins(:roles).where(roles: { name: 'Manager' }) }, class_name: 'Member' | |||
| has_many :developers, -> { joins(:roles).where(roles: { name: 'Developer' }) }, class_name: 'Member' | |||
| has_many :reporters, -> { joins(:roles).where(roles: { name: 'Reporter' }) }, class_name: 'Member' | |||
| has_many :writable_members, -> { joins(:roles).where.not(roles: {name: 'Reporter'}) }, class_name: 'Member' | |||
| end | |||
| def add_member!(user_id, role_name='Developer') | |||
| @@ -1,5 +1,18 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: coo_imgs | |||
| # | |||
| # id :integer not null, primary key | |||
| # src_states :string(255) | |||
| # url_states :string(255) | |||
| # img_type :string(255) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # position :integer | |||
| # | |||
| class CooImg < ApplicationRecord | |||
| extend Enumerize | |||
| enumerize :img_type, in: %i[com_coop edu_coop alliance_coop] | |||
| end | |||
| end | |||
| @@ -1,3 +1,21 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: diff_records | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer | |||
| # container_type :string(255) | |||
| # container_id :integer | |||
| # column_name :string(255) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_diff_records_on_container_type_and_container_id (container_type,container_id) | |||
| # index_diff_records_on_user_id (user_id) | |||
| # | |||
| class DiffRecord < ApplicationRecord | |||
| belongs_to :user | |||
| belongs_to :container, polymorphic: true | |||
| @@ -5,4 +23,4 @@ class DiffRecord < ApplicationRecord | |||
| has_one :diff_record_content, dependent: :destroy | |||
| delegate :content, to: :diff_record_content | |||
| end | |||
| end | |||
| @@ -1,3 +1,16 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: diff_record_contents | |||
| # | |||
| # id :integer not null, primary key | |||
| # diff_record_id :integer | |||
| # content :text(65535) | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_diff_record_contents_on_diff_record_id (diff_record_id) | |||
| # | |||
| class DiffRecordContent < ApplicationRecord | |||
| belongs_to :diff_record | |||
| end | |||
| end | |||
| @@ -1,3 +1,19 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: edu_settings | |||
| # | |||
| # id :integer not null, primary key | |||
| # name :string(255) | |||
| # value :string(255) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # description :string(255) | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_edu_settings_on_name (name) UNIQUE | |||
| # | |||
| class EduSetting < ApplicationRecord | |||
| after_commit :expire_value_cache | |||
| @@ -1,4 +1,23 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: fork_users | |||
| # | |||
| # id :integer not null, primary key | |||
| # project_id :integer | |||
| # fork_project_id :integer | |||
| # user_id :integer | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_fork_users_on_project_id (project_id) | |||
| # index_fork_users_on_user_id (user_id) | |||
| # | |||
| class ForkUser < ApplicationRecord | |||
| belongs_to :project | |||
| belongs_to :project | |||
| belongs_to :user | |||
| belongs_to :fork_project, class_name: 'Project', foreign_key: :fork_project_id | |||
| end | |||
| @@ -0,0 +1,4 @@ | |||
| class Gitea::Base < Gitea::Database | |||
| self.abstract_class = true | |||
| end | |||
| @@ -1,3 +1,14 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: ignores | |||
| # | |||
| # id :integer not null, primary key | |||
| # name :string(255) | |||
| # content :text(65535) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| class Ignore < ApplicationRecord | |||
| include Projectable | |||
| end | |||
| @@ -1,3 +1,53 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: issues | |||
| # | |||
| # id :integer not null, primary key | |||
| # tracker_id :integer not null | |||
| # project_id :integer not null | |||
| # subject :string(255) default(""), not null | |||
| # description :text(4294967295) | |||
| # due_date :date | |||
| # category_id :integer | |||
| # status_id :integer not null | |||
| # assigned_to_id :integer | |||
| # priority_id :integer not null | |||
| # fixed_version_id :integer | |||
| # author_id :integer not null | |||
| # created_on :datetime | |||
| # updated_on :datetime | |||
| # start_date :date | |||
| # done_ratio :integer default("0"), not null | |||
| # estimated_hours :float(24) | |||
| # parent_id :integer | |||
| # root_id :integer | |||
| # lft :integer | |||
| # rgt :integer | |||
| # is_private :boolean default("0"), not null | |||
| # closed_on :datetime | |||
| # project_issues_index :integer | |||
| # issue_type :string(255) | |||
| # token :integer default("0") | |||
| # issue_tags_value :string(255) | |||
| # is_lock :boolean default("0") | |||
| # issue_classify :string(255) | |||
| # ref_name :string(255) | |||
| # branch_name :string(255) | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_issues_on_assigned_to_id (assigned_to_id) | |||
| # index_issues_on_author_id (author_id) | |||
| # index_issues_on_category_id (category_id) | |||
| # index_issues_on_created_on (created_on) | |||
| # index_issues_on_fixed_version_id (fixed_version_id) | |||
| # index_issues_on_priority_id (priority_id) | |||
| # index_issues_on_root_id_and_lft_and_rgt (root_id,lft,rgt) | |||
| # index_issues_on_status_id (status_id) | |||
| # index_issues_on_tracker_id (tracker_id) | |||
| # issues_project_id (project_id) | |||
| # | |||
| class Issue < ApplicationRecord | |||
| #issue_type 1为普通,2为悬赏 | |||
| include DunCheckAble | |||
| @@ -1,3 +1,19 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: issue_depends | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer | |||
| # issue_id :integer | |||
| # depend_issue_id :integer | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_issue_depends_on_user_id_and_issue_id_and_depend_issue_id (user_id,issue_id,depend_issue_id) | |||
| # | |||
| class IssueDepend < ApplicationRecord | |||
| belongs_to :issue | |||
| end | |||
| @@ -1,3 +1,18 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: issue_priorities | |||
| # | |||
| # id :integer not null, primary key | |||
| # name :string(255) | |||
| # position :integer | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_issue_priorities_on_name (name) | |||
| # | |||
| class IssuePriority < ApplicationRecord | |||
| has_many :issues | |||
| end | |||
| end | |||
| @@ -1,4 +1,22 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: issue_statuses | |||
| # | |||
| # id :integer not null, primary key | |||
| # name :string(30) default(""), not null | |||
| # is_closed :boolean default("0"), not null | |||
| # is_default :boolean default("0"), not null | |||
| # position :integer default("1") | |||
| # default_done_ratio :integer | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_issue_statuses_on_is_closed (is_closed) | |||
| # index_issue_statuses_on_is_default (is_default) | |||
| # index_issue_statuses_on_position (position) | |||
| # | |||
| class IssueStatus < ApplicationRecord | |||
| has_many :issues | |||
| belongs_to :project, optional: true | |||
| end | |||
| end | |||
| @@ -1,3 +1,24 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: issue_tags | |||
| # | |||
| # id :integer not null, primary key | |||
| # name :string(255) | |||
| # description :string(255) | |||
| # color :string(255) | |||
| # user_id :integer | |||
| # project_id :integer | |||
| # issues_count :integer default("0") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # gid :integer | |||
| # gitea_url :string(255) | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_issue_tags_on_user_id_and_name_and_project_id (user_id,name,project_id) | |||
| # | |||
| class IssueTag < ApplicationRecord | |||
| has_many :issue_tags_relates, dependent: :destroy | |||
| @@ -1,3 +1,18 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: issue_tags_relates | |||
| # | |||
| # id :integer not null, primary key | |||
| # issue_id :integer | |||
| # issue_tag_id :integer | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_issue_tags_relates_on_issue_id_and_issue_tag_id (issue_id,issue_tag_id) | |||
| # | |||
| class IssueTagsRelate < ApplicationRecord | |||
| belongs_to :issue | |||
| belongs_to :issue_tag, counter_cache: :issues_count | |||