diff --git a/README.md b/README.md index 1013d6f95..8a6ada33d 100644 --- a/README.md +++ b/README.md @@ -2357,6 +2357,51 @@ http://localhost:3000/api//api/repositories/3868/delete_file | jq ### DevOps相关api --- +#### 获取devops流程步骤(判断devops是否初始化) +``` +GET /api/users/devops +``` + +*示例* +``` +curl -X GET \ +-d "project_id=5988" \ +https://localhost:3000/api/users/devops.json | jq +``` + +*请求参数说明:* + +|参数名|必选|类型|说明| +|-|-|-|-| +|project_id |是|string |项目id或者项目的标识identifier| + +*返回参数说明:* + +|参数名|类型|说明| +|-|-|-| +|step |int|初始化devops流程步骤; 0: 标识未开启devops,1: 标识用户已填写了云服务器相关信息,但并未开启认证, 2: 标识用户已开启了CI服务端的认证, 3: 标识用户已经授权并获取了CI服务的token| +|account |string|你的云服务器帐号| +|ip |string|你的云服务器帐号ip| +|secret |string|你的云服务器登录密码| +|authenticate_url |string|devops授权认证地址, 只有填写了服务器相关信息后才会有该地址| +|get_drone_token_url |string|获取CI服务端token地址, 只有认证成功后才会有该地址| + +返回值 +```json +{ + "step": 0, + "cloud_account": { + "id": 1, + "account": "xxx", + "ip": "xxx.xxx.xxx.x", + "secret": "11111", + "authenticate_url": "http://localhost:3000/login", + "get_drone_token_url": "http://localhost:3000/account" + } +} +``` +--- + #### 初始化DevOps流程 ``` POST /api/dev_ops/cloud_accounts @@ -2399,6 +2444,72 @@ https://localhost:3000/api/dev_ops/cloud_accounts.json | jq ``` --- +#### 用户认证CI服务端后,需要调用该接口进行更新devlops流程状态 +``` +PUT /api/users/devops_authenticate +``` +*示例* +``` +curl -X PUT \ +-d "project_id=5988" \ +http://localhost:3000/api/users/devops_authenticate.json | jq +``` +*请求参数说明:* + +|参数名|必选|类型|说明| +|-|-|-|-| +|project_id |是|string |项目id或者项目的标识identifier| + + +*返回参数说明:* + +|参数名|类型|说明| +|-|-|-| +|status |int|0:成功, -1: 失败| + +``` +{ + "status": 0, + "message": "success" +} +``` +--- + +#### 激活项目 +``` +POST /api/dev_ops/cloud_accounts/:id/activate +``` +*示例* +``` +curl -X POST \ +-d "id=1" \ +-d "project_id=4844" \ +-d "drone_token=xxxxxxxxxx" \ +http://localhost:3000/api/dev_ops/cloud_accounts/1/activate.json | jq +``` +*请求参数说明:* + +|参数名|必选|类型|说明| +|-|-|-|-| +|project_id |是|int |project's id or identifier | +|id |是|int |cloud_account's id | +|drone_token |否|string |CI端用户的token值,只有当用户第一次激活时,才需要填写该值 | + + +*返回参数说明:* + +|参数名|类型|说明| +|-|-|-| +|status |int|0:成功, -1: 失败| + +``` +{ + "status": 0, + "message": "success" +} +``` +--- + #### 获取仓库的.trustie-pipeline.yml ``` GET /api/dev_ops/builds/get_trustie_pipeline @@ -2433,6 +2544,7 @@ http://localhost:3000/api/dev_ops/builds/get_trustie_pipeline.json | jq "content": "..jsaf" } ``` +--- #### 获取语言列表 ``` diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e97c8ec08..9cb23ab2d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -744,11 +744,6 @@ class ApplicationController < ActionController::Base interactor.success? ? render_ok : render_error(interactor.error) end - # devops 权限验证 - def devops_authorize! - render_forbidden unless @project.owner?(current_user) - end - private def object_not_found uid_logger("Missing template or cant't find record, responding with 404") diff --git a/app/controllers/concerns/devopsable.rb b/app/controllers/concerns/devopsable.rb new file mode 100644 index 000000000..43828fd22 --- /dev/null +++ b/app/controllers/concerns/devopsable.rb @@ -0,0 +1,32 @@ +module Devopsable + extend ActiveSupport::Concern + + included do + end + + # devops 权限验证 + def devops_authorize! + render_forbidden unless @project.owner?(current_user) + end + + def auto_load_project + @project = Project.find_by(id: params[:project_id]) || Project.find_by(identifier: params[:project_id]) + render_not_found('未找到相关的项目') if @project.blank? + end + + # TODO 暂时限制项目拥有者才有权限操作 + def limit_owner_can_devops!(user) + return if @project.owner? user + render_forbidden + end + + def find_cloud_account + @cloud_account = DevOps::CloudAccount.find params[:id] + end + + def set_drone_token!(user, cloud_account, drone_token) + return if user.devops_has_token? + cloud_account.update_column(:drone_token, drone_token) + user.set_drone_step!(User::DEVOPS_HAS_TOKEN) + end +end diff --git a/app/controllers/dev_ops/cloud_accounts_controller.rb b/app/controllers/dev_ops/cloud_accounts_controller.rb index 10cd67bbd..d2e849eb5 100644 --- a/app/controllers/dev_ops/cloud_accounts_controller.rb +++ b/app/controllers/dev_ops/cloud_accounts_controller.rb @@ -1,7 +1,10 @@ class DevOps::CloudAccountsController < ApplicationController + include Devopsable + before_action :require_login - before_action :find_project + before_action :auto_load_project before_action :devops_authorize! + before_action :find_cloud_account, only: %i[activate] def create ActiveRecord::Base.transaction do @@ -14,8 +17,6 @@ class DevOps::CloudAccountsController < ApplicationController else cloud_account = DevOps::CloudAccount.new(create_params) cloud_account.user = current_user - cloud_account.repo_id = @project.repository.id - cloud_account.project_id = @project.id cloud_account.save! end @@ -50,6 +51,7 @@ class DevOps::CloudAccountsController < ApplicationController logger.info "######### redirect_url: #{redirect_url}" if result && !result.blank? + current_user.set_drone_step!(User::DEVOPS_UNVERIFIED) render_ok(redirect_url: redirect_url) else render_error('激活失败, 请检查你的云服务器信息是否正确.') @@ -60,12 +62,28 @@ class DevOps::CloudAccountsController < ApplicationController render_error(ex.message) end + def activate + result = + if current_user.devops_has_token? + # 已有drone_token的,直接激活项目 + DevOps::Drone::API.new(@cloud_account.drone_token, @cloud_account.drone_url, @project.owner.login, @project.identifier).activate + else + # 没有token,说明是第一次激活devops, 需要用户填写token值 + return render_error('请先在CI服务端做用户认证.') if !current_user.devops_verified? + DevOps::Drone::API.new(params[:drone_token], @cloud_account.drone_url, @project.owner.login, @project.identifier).activate + end + + if result + set_drone_token!(current_user, @cloud_account, params[:drone_token]) + @project.update_column(:open_devops, true) + render_ok + else + render_error("激活失败,请检查你的token值是否正确.") + end + end + private def devops_params params.permit(:account, :secret, :ip_num, :project_id) end - - def find_project - @project = Project.find params[:project_id] - end end diff --git a/app/controllers/dev_ops/languages_controller.rb b/app/controllers/dev_ops/languages_controller.rb index 5863be647..be8b25b85 100644 --- a/app/controllers/dev_ops/languages_controller.rb +++ b/app/controllers/dev_ops/languages_controller.rb @@ -1,4 +1,5 @@ class DevOps::LanguagesController < ApplicationController + # TODO 需要开启权限认证,只有该项目devops初始化成功后才能获取语言列表 before_action :require_login before_action :find_langugae, only: :show diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index ac60f4d18..00b1e37a5 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,8 +1,10 @@ class UsersController < ApplicationController + include Devopsable 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 :require_login, only: %i[me list devops_authenticate devops] + before_action :auto_load_project, only: %i[devops devops_authenticate] skip_before_action :check_sign, only: [:attachment_show] def list @@ -177,7 +179,7 @@ class UsersController < ApplicationController def trustie_projects user_id = User.select(:id, :login).where(login: params[:login])&.first&.id projects = Project.visible - + projects = projects.joins(:members).where(members: { user_id: user_id }) search = params[:search].to_s.strip @@ -212,6 +214,19 @@ class UsersController < ApplicationController render_ok end + def devops + @user = current_user + limit_owner_can_devops!(user) + @cloud_account = @user.dev_ops_cloud_account + end + + # devops 认证 + def devops_authenticate + limit_owner_can_devops!(current_user) + current_user.set_drone_step!(User::DEVOPS_VERIFIED) + render_ok + end + private def load_user @user = User.find_by_login(params[:id]) || User.find_by(id: params[:id]) diff --git a/app/libs/dev_ops/drone/ci.rb b/app/libs/dev_ops/drone/ci.rb index cfa67cdc2..c2a06d4ce 100644 --- a/app/libs/dev_ops/drone/ci.rb +++ b/app/libs/dev_ops/drone/ci.rb @@ -20,6 +20,6 @@ class DevOps::Drone::Ci private def cmd - "cd ..; cd var/lib/drone/; sqlite3 database.sqlite; .dump; select user_hash from users where user_login=#{gitea_username} " + "cd ..; cd var/lib/drone/; sqlite3 database.sqlite; .dump; select user_hash from users where user_login=#{gitea_username};" end end diff --git a/app/libs/dev_ops/drone/server.rb b/app/libs/dev_ops/drone/server.rb index cb08ca271..152f27df4 100644 --- a/app/libs/dev_ops/drone/server.rb +++ b/app/libs/dev_ops/drone/server.rb @@ -18,7 +18,8 @@ class DevOps::Drone::Server def generate_cmd "service docker start; docker rm -f `docker ps -qa`; docker run \ -v /var/run/docker.sock:/var/run/docker.sock \ - -v /var/lib/drone:/data \ + -e DRONE_DATABASE_DRIVER=mysql \ + -e DRONE_DATABASE_DATASOURCE=#{database_username}:#{database_password}@#{database_host}:3306/drone?parseTime=true \ -e DRONE_GITEA_SERVER=#{gitea_url} \ -e DRONE_GITEA_CLIENT_ID=#{client_id} \ -e DRONE_GITEA_CLIENT_SECRET=#{client_secret} \ @@ -37,4 +38,24 @@ class DevOps::Drone::Server def gitea_url Gitea.gitea_config[:domain] end + + def database_username + database_config[Rails.env]["username"] + end + + def database_password + database_config[Rails.env]["password"] + end + + def database_host + database_config[Rails.env]["host"] + end + + def database + database_config[Rails.env]["database"] + end + + def database_config + Rails.configuration.database_configuration + end end diff --git a/app/models/concerns/droneable.rb b/app/models/concerns/droneable.rb new file mode 100644 index 000000000..2e7206fb0 --- /dev/null +++ b/app/models/concerns/droneable.rb @@ -0,0 +1,29 @@ +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_verified? + self.devops_step === User::DEVOPS_VERIFIED + end + + def devops_has_token? + self.devops_step === User::DEVOPS_HAS_TOKEN + end + + def set_drone_step!(step) + self.update_column(:devops_step, step) + end + + module ClassMethods + end +end diff --git a/app/models/user.rb b/app/models/user.rb index f919ed1bf..9d6b24694 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,8 +5,16 @@ class User < ApplicationRecord include Likeable include BaseModel include ProjectOperable + include Droneable # include Searchable::Dependents::User + # devops step + # devops_step column: 0: 未填写服务器信息;1: 已填写服务器信息(未认证); 2: 已认证, 3: 已填写token值 + DEVOPS_UNINIT = 0 + DEVOPS_UNVERIFIED = 1 + DEVOPS_VERIFIED = 2 + DEVOPS_HAS_TOKEN = 3 + # Account statuses STATUS_ANONYMOUS = 0 STATUS_ACTIVE = 1 @@ -70,8 +78,9 @@ class User < ApplicationRecord # 关注 has_many :be_watchers, foreign_key: :user_id, dependent: :destroy # 我的关注 has_many :be_watcher_users, through: :be_watchers, dependent: :destroy # 我关注的用户 - - has_many :watchers, as: :watchable, dependent: :destroy + has_many :watchers, as: :watchable, dependent: :destroy + + has_one :dev_ops_cloud_account, class_name: 'DevOps::CloudAccount', dependent: :destroy # 认证 has_many :apply_user_authentication diff --git a/app/views/users/devops.json.jbuilder b/app/views/users/devops.json.jbuilder new file mode 100644 index 000000000..7ae5fa2c1 --- /dev/null +++ b/app/views/users/devops.json.jbuilder @@ -0,0 +1,12 @@ +json.step @user.devops_step +json.cloud_account do + if @cloud_account && !@user.devops_uninit? + json.account @cloud_account.account + json.ip @cloud_account.drone_ip + json.secret @cloud_account.visible_secret + json.authenticate_url "#{@cloud_account.drone_url}/login" if @user.devops_unverified? + json.get_drone_token_url "#{@cloud_account.drone_url}/account" if @user.devops_verified? + else + json.nil! + end +end diff --git a/app/views/users/get_user_info.json.jbuilder b/app/views/users/get_user_info.json.jbuilder index 1c83716bd..1c78d4fef 100644 --- a/app/views/users/get_user_info.json.jbuilder +++ b/app/views/users/get_user_info.json.jbuilder @@ -12,5 +12,4 @@ json.user_phone_binded @user.phone.present? # json.email @user.mail json.profile_completed @user.profile_completed? json.professional_certification @user.professional_certification - - +json.devops_step @user.devops_step diff --git a/config/routes.rb b/config/routes.rb index 8064352bf..e292a396c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,7 +16,11 @@ Rails.application.routes.draw do resources :edu_settings scope '/api' do namespace :dev_ops do - resources :cloud_accounts, only: [:create] + resources :cloud_accounts, only: [:create] do + member do + post :activate + end + end resources :languages, only: [:index, :show] do collection do get :common @@ -189,11 +193,12 @@ Rails.application.routes.draw do post :sync_salt get :trustie_projects get :trustie_related_projects + get :devops + put :devops_authenticate end scope module: :users do - # resources :courses, only: [:index] - resources :projects, only: [:index] + # resources :projects, only: [:index] # resources :subjects, only: [:index] resources :project_packages, only: [:index] # 私信 diff --git a/db/migrate/20200731073851_add_devops_step_to_users.rb b/db/migrate/20200731073851_add_devops_step_to_users.rb new file mode 100644 index 000000000..6815b797e --- /dev/null +++ b/db/migrate/20200731073851_add_devops_step_to_users.rb @@ -0,0 +1,5 @@ +class AddDevopsStepToUsers < ActiveRecord::Migration[5.2] + def change + add_column :users, :devops_step, :integer, default: 0, comment: '0: uninit devops; 1: unverified; 2: verified' + end +end