| @@ -2424,7 +2424,7 @@ http://localhost:3000/api/jasder/forgeplus/ci_authorize.json | jq | |||
| |参数名|类型|说明| | |||
| |-|-|-| | |||
| |step |int|初始化devops流程步骤; 0: 标识未开启devops,1: 标识用户已填写了云服务器相关信息,但并未开启认证, 2: 标识用户已开启了CI服务端的认证, 3: 标识用户ci服务已初始化| | |||
| |step |int|初始化devops流程步骤; 0: 标识未开启devops,1: 标识用户已填写了云服务器相关信息,但并未开启认证, 2: 标识用户已开启了CI服务端的认证| | |||
| |account |string|你的云服务器帐号| | |||
| |ip |string|你的云服务器帐号ip| | |||
| |secret |string|你的云服务器登录密码| | |||
| @@ -3108,3 +3108,182 @@ http://localhost:3000/api/dev_ops/builds/2/logs/1/1 | jq | |||
| ] | |||
| ``` | |||
| --- | |||
| #### 获取CI服务器配置信息 | |||
| ``` | |||
| GET /api/users/ci/cloud_account | |||
| ``` | |||
| *示例* | |||
| ``` | |||
| curl -X GET \ | |||
| http://localhost:3000/api/users/ci/cloud_account | jq | |||
| ``` | |||
| *返回参数说明:* | |||
| |参数名|类型|说明| | |||
| |-|-|-| | |||
| |step |int|0: 未绑定;1: 未认证(已绑定),2: 已认证| | |||
| |ip |string|ci服务器ip| | |||
| |redirect_url |string|认证地址| | |||
| 返回值 | |||
| ```json | |||
| { | |||
| "step": 0, | |||
| "cloud_account": { | |||
| "ip": "xxx.xxx.xxx.x", | |||
| "redirect_url": "http://localhost:3000/login", | |||
| } | |||
| } | |||
| ``` | |||
| --- | |||
| #### 绑定CI服务器 | |||
| ``` | |||
| POST /api/users/ci/cloud_account/bind | |||
| ``` | |||
| *示例* | |||
| ``` | |||
| curl -X POST \ | |||
| -d "account=xx" \ | |||
| -d "secret=xxx" \ | |||
| -d "ip_num=xx.xx.xx.xx" \ | |||
| https://localhost:3000/api/users/ci/cloud_account/bind.json | jq | |||
| ``` | |||
| *请求参数说明:* | |||
| |参数名|必选|类型|说明| | |||
| |-|-|-|-| | |||
| |account |是|string |云服务器ssh连接登录用户名 | | |||
| |secret |是|string |云服务器ssh连接登录秘密 | | |||
| |ip_num |否|string |云服务器公网IP | | |||
| *返回参数说明:* | |||
| |参数名|类型|说明| | |||
| |-|-|-| | |||
| |step |int|0: 未绑定;1: 未认证(已绑定),2: 已认证| | |||
| |ip |string|ci服务器ip| | |||
| |redirect_url |string|认证地址| | |||
| 返回值 | |||
| ```json | |||
| { | |||
| "step": 0, | |||
| "cloud_account": { | |||
| "ip": "xxx.xxx.xxx.x", | |||
| "redirect_url": "http://localhost:3000/login", | |||
| } | |||
| } | |||
| ``` | |||
| --- | |||
| ### 解除CI服务器绑定 | |||
| ``` | |||
| DELETE /api/users/ci/cloud_account/unbind | |||
| ``` | |||
| *示例* | |||
| ``` | |||
| curl -X DELETE \ | |||
| http://localhost:3000/api/users/ci/cloud_account/unbind.json | jq | |||
| ``` | |||
| *返回参数说明:* | |||
| |参数名|类型|说明| | |||
| |-|-|-| | |||
| |status |int|状态码, 0: 成功,-1: 失败| | |||
| |message |string|返回信息说明| | |||
| 返回值 | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| --- | |||
| ### 项目列表 | |||
| ``` | |||
| GET /api/users/:login/projects | |||
| ``` | |||
| *示例* | |||
| ``` | |||
| curl -X GET \ | |||
| -d "page=1" \ | |||
| -d "limit=20" \ | |||
| http://localhost:3000/api/users/Jason/projects.json | jq | |||
| ``` | |||
| *请求参数说明:* | |||
| |参数名|必选|类型|说明| | |||
| |-|-|-|-| | |||
| |page |否|int |页数,第几页 | | |||
| |limit |否|int |每页多少条数据,默认20条 | | |||
| *返回参数说明:* | |||
| |参数名|类型|说明| | |||
| |-|-|-| | |||
| |total_count |int |项目总条数 | | |||
| |id |string |项目id | | |||
| |name |string|项目名称| | |||
| |description |string|项目简介| | |||
| |open_devops |boolean|激活状态,true: 激活; false:未激活| | |||
| |visits |int|流量数| | |||
| |forked_count |int|被fork的数量| | |||
| |praises_count |int|star数量| | |||
| |is_public |boolean|是否公开, true:公开,false:未公开| | |||
| |mirror_url |string|镜像url| | |||
| |last_update_time|int|最后更新时间,为UNIX格式的时间戳| | |||
| |author |object|项目创建者| | |||
| |-- name |string|用户名,也是用户标识| | |||
| |category |object|项目类别| | |||
| |-- id |int|项目类型id| | |||
| |-- name |string|项目类型名称| | |||
| |language |object|项目语言| | |||
| |-- id |int|项目语言id| | |||
| |-- name |string|项目语言名称| | |||
| 返回值 | |||
| ``` | |||
| { | |||
| "total_count": 3096, | |||
| "projects": [ | |||
| { | |||
| "id": 1, | |||
| "name": "hnfl_demo1", | |||
| "description": "my first project", | |||
| "visits": 0, | |||
| "praises_count": 0, | |||
| "forked_count": 0, | |||
| "is_public": true, | |||
| "mirror_url": null, | |||
| "last_update_time": 1577697461, | |||
| "author": { | |||
| "name": "18816895620", | |||
| "image_url": "avatars/User/b" | |||
| }, | |||
| "category": { | |||
| "id": 1, | |||
| "name": "大数据" | |||
| }, | |||
| "language": { | |||
| "id": 2, | |||
| "name": "C" | |||
| } | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| --- | |||
| @@ -10,25 +10,93 @@ class Ci::CloudAccountsController < Ci::BaseController | |||
| ActiveRecord::Base.transaction do | |||
| Ci::CreateCloudAccountForm.new(devops_params).validate! | |||
| # 1. 保存华为云服务器帐号 | |||
| create_params = devops_params.merge(ip_num: IPAddr.new(devops_params[:ip_num]).to_i, secret: Ci::CloudAccount.encrypted_secret(devops_params[:secret])) | |||
| if current_user&.ci_cloud_account.present? | |||
| return render_error('该仓库已绑定了云帐号.') | |||
| @cloud_account = bind_account(current_user) | |||
| 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 activate | |||
| return render_error('请先在指定地址做用户认证') unless current_user.ci_certification? | |||
| return render_error('该项目已经激活') if @repo && @repo.repo_active? | |||
| ci_user = Ci::User.find_by(user_login: current_user.login) | |||
| repo = Ci::Repo.where(repo_namespace: current_user.login, repo_name: params[:repo]).first | |||
| begin | |||
| repo.activate!(ci_user.user_id) | |||
| @project.update_column(:open_devops, true) | |||
| @cloud_account.update_column(:ci_user_id, ci_user.user_id) | |||
| render_ok | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| end | |||
| def show | |||
| @cloud_account = current_user.ci_cloud_account | |||
| end | |||
| def bind | |||
| Ci::CreateCloudAccountForm.new(devops_params).validate! | |||
| ActiveRecord::Base.transaction do | |||
| @cloud_account = bind_account(current_user) | |||
| if @cloud_account.blank? | |||
| render_error('激活失败, 请检查你的云服务器信息是否正确.') | |||
| raise ActiveRecord::Rollback | |||
| else | |||
| cloud_account = Ci::CloudAccount.new(create_params) | |||
| cloud_account.user = current_user | |||
| cloud_account.save! | |||
| 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!(current_user) | |||
| render_ok | |||
| end | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| private | |||
| def devops_params | |||
| params.permit(:account, :secret, :ip_num) | |||
| end | |||
| def find_cloud_account | |||
| @cloud_account = Ci::CloudAccount.find params[:id] | |||
| end | |||
| def bind_account(user) | |||
| # 1. 保存华为云服务器帐号 | |||
| create_params = devops_params.merge(ip_num: IPAddr.new(devops_params[:ip_num]).to_i, secret: Ci::CloudAccount.encrypted_secret(devops_params[:secret])) | |||
| return render_error('你已绑定了云帐号.') if user.ci_cloud_account.blank? | |||
| cloud_account = Ci::CloudAccount.new(create_params) | |||
| cloud_account.user = user | |||
| cloud_account.save! | |||
| # 2. 生成oauth2应用程序的client_id和client_secrete | |||
| gitea_oauth = Gitea::Oauth2::CreateService.call(current_user.gitea_token, {name: "pipeline", redirect_uris: ["#{cloud_account.drone_url}/login"]}) | |||
| gitea_oauth = Gitea::Oauth2::CreateService.call(user.gitea_token, {name: "pipeline", 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, | |||
| project_id: @project.id) | |||
| user_id: current_user.id) | |||
| oauth.save | |||
| rpc_secret = SecureRandom.hex 16 | |||
| @@ -50,41 +118,21 @@ class Ci::CloudAccountsController < Ci::BaseController | |||
| redirect_url = "#{cloud_account.drone_url}/login" | |||
| 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('激活失败, 请检查你的云服务器信息是否正确.') | |||
| raise ActiveRecord::Rollback | |||
| end | |||
| result && !result.blank? ? cloud_account : nil | |||
| end | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| def activate | |||
| return render_error('请先在指定地址做用户认证') unless current_user.ci_certification? | |||
| return render_error('该项目已经激活') if @repo && @repo.repo_active? | |||
| def unbind_account!(user) | |||
| cloud_account = user.ci_cloud_account | |||
| ci_user = Ci::User.find_by(user_login: current_user.login) | |||
| repo = Ci::Repo.where(repo_namespace: current_user.login, repo_name: params[:repo]).first | |||
| begin | |||
| repo.activate!(ci_user.user_id) | |||
| @project.update_column(:open_devops, true) | |||
| @cloud_account.update_column(:ci_user_id, ci_user.user_id) | |||
| render_ok | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| end | |||
| private | |||
| def devops_params | |||
| params.permit(:account, :secret, :ip_num) | |||
| end | |||
| def find_cloud_account | |||
| @cloud_account = Ci::CloudAccount.find params[:id] | |||
| case user.devops_step | |||
| when User::DEVOPS_UNINIT | |||
| return render_error('你还未绑定CI服务器') | |||
| when User::DEVOPS_UNVERIFIED | |||
| cloud_account.destroy | |||
| when User::DEVOPS_CERTIFICATION | |||
| cloud_account.ci_user.destroy | |||
| end | |||
| user.projects.update_all(open_devops: false) | |||
| user.set_drone_step!(User::DEVOPS_UNINIT) | |||
| end | |||
| end | |||
| @@ -1,5 +1,6 @@ | |||
| class Ci::CloudAccount < Ci::LocalBase | |||
| belongs_to :user | |||
| belongs_to :ci_user, class_name: 'Ci::User', foreign_key: :ci_user_id | |||
| def drone_host | |||
| [drone_ip, ":80"].join | |||
| @@ -0,0 +1,4 @@ | |||
| class Ci::Log < Ci::RemoteBase | |||
| self.primary_key = 'log_id' | |||
| end | |||
| @@ -0,0 +1,4 @@ | |||
| class Ci::Perm < Ci::RemoteBase | |||
| belongs_to :user, foreign_key: :perm_user_id | |||
| end | |||
| @@ -2,5 +2,7 @@ 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: :destroy | |||
| has_one :ci_cloud_account, class_name: 'Ci::CloudAccount', foreign_key: :ci_user_id | |||
| end | |||
| @@ -1,5 +1,4 @@ | |||
| # for oauth2 application | |||
| class Oauth < ApplicationRecord | |||
| belongs_to :project | |||
| belongs_to :user | |||
| end | |||
| @@ -10,9 +10,10 @@ class User < ApplicationRecord | |||
| # include Searchable::Dependents::User | |||
| # devops step | |||
| # devops_step column: 0: 未填写服务器信息;1: 已填写服务器信息(未认证); | |||
| # devops_step column: 0: 未填写服务器信息;1: 已填写服务器信息(未认证);2: 已认证 | |||
| DEVOPS_UNINIT = 0 | |||
| DEVOPS_UNVERIFIED = 1 | |||
| DEVOPS_CERTIFICATION = 2 | |||
| # Account statuses | |||
| STATUS_ANONYMOUS = 0 | |||
| @@ -0,0 +1,9 @@ | |||
| json.step current_user.devops_step | |||
| json.cloud_account do | |||
| if @cloud_account && !current_user.devops_uninit? | |||
| json.ip @cloud_account.drone_ip | |||
| json.redirect_url "#{@cloud_account.drone_url}/login" if current_user.devops_unverified? | |||
| else | |||
| json.nil! | |||
| end | |||
| end | |||
| @@ -0,0 +1,9 @@ | |||
| json.step current_user.devops_step | |||
| json.cloud_account do | |||
| if @cloud_account && !current_user.devops_uninit? | |||
| json.ip @cloud_account.drone_ip | |||
| json.redirect_url "#{@cloud_account.drone_url}/login" if current_user.devops_unverified? | |||
| else | |||
| json.nil! | |||
| end | |||
| end | |||
| @@ -16,6 +16,7 @@ json.type project&.numerical_for_project_type | |||
| json.last_update_time render_unix_time(project.updated_on) | |||
| json.time_ago time_from_now(project.updated_on) | |||
| json.forked_from_project_id project.forked_from_project_id | |||
| json.open_devops project.open_devops? | |||
| json.author do | |||
| json.name user.try(:show_real_name) | |||
| json.login user.login | |||
| @@ -26,6 +26,7 @@ Rails.application.routes.draw do | |||
| get :common | |||
| end | |||
| end | |||
| # resources :repos, only: :index do | |||
| # collection do | |||
| # get 'get_trustie_pipeline', to: 'builds#get_trustie_pipeline', as: 'get_trustie_pipeline' | |||
| @@ -144,6 +145,28 @@ Rails.application.routes.draw do | |||
| post :sync_salt | |||
| get :trustie_projects | |||
| get :trustie_related_projects | |||
| scope '/ci', module: :ci do | |||
| scope do | |||
| post( | |||
| '/cloud_account/bind', | |||
| to: 'cloud_accounts#bind', | |||
| as: :bind_cloud_acclount | |||
| ) | |||
| get( | |||
| '/cloud_account', | |||
| to: 'cloud_accounts#show', | |||
| as: :get_cloud_account | |||
| ) | |||
| delete( | |||
| '/cloud_account/unbind', | |||
| to: 'cloud_accounts#unbind', | |||
| as: :unbind_cloud_acclount | |||
| ) | |||
| end | |||
| end | |||
| end | |||
| scope module: :users do | |||