Merge branch 'standalone_develop' into pre_trustie_server

This commit is contained in:
yystopf 2022-05-20 17:28:25 +08:00
commit dc0d31330a
32 changed files with 1079 additions and 205 deletions

3
.gitignore vendored
View File

@ -84,4 +84,5 @@ redis_data/
Dockerfile Dockerfile
dump.rdb dump.rdb
.tags* .tags*
ceshi_user.xlsx ceshi_user.xlsx
public/trace_task_results

View File

@ -2,7 +2,7 @@ class Admins::ImportUsersController < Admins::BaseController
def create def create
return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile) return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile)
result = Admins::ImportUserService.call(params[:file].to_io) result = Admins::ImportUserFromExcelService.call(params[:file].to_io)
render_ok(result) render_ok(result)
rescue Admins::ImportUserService::Error => ex rescue Admins::ImportUserService::Error => ex
render_error(ex) render_error(ex)

View File

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

View File

@ -22,6 +22,7 @@ class ProjectsController < ApplicationController
menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops") && @project.forge? menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops") && @project.forge?
menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions") menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions")
menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki") && @project.forge? menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki") && @project.forge?
menu.append(menu_hash_by_name("services")) if @project.has_menu_permission("services") && @project.forge? && (current_user.admin? || @project.member?(current_user.id))
menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources") && @project.forge? menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources") && @project.forge?
menu.append(menu_hash_by_name("activity")) menu.append(menu_hash_by_name("activity"))
menu.append(menu_hash_by_name("settings")) if user_is_admin && @project.forge? menu.append(menu_hash_by_name("settings")) if user_is_admin && @project.forge?

View File

@ -225,7 +225,8 @@ class RepositoriesController < ApplicationController
@path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/" @path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
@readme = result[:status] === :success ? result[:body] : nil @readme = result[:status] === :success ? result[:body] : nil
@readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path) @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha") @readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path)
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha", "replace_content")
rescue rescue
render json: nil render json: nil
end end
@ -387,4 +388,4 @@ class RepositoriesController < ApplicationController
end end
end end
end end

View File

@ -0,0 +1,18 @@
class Traces::BaseController < ApplicationController
helper_method :observed_logged_user?, :observed_user
def observed_user
@_observed_user ||= (User.find_by_login(params[:user_id]) || User.find_by_id(params[:user_id]))
end
def observed_logged_user?
observed_user.id == User.current&.id
end
protected
def check_auth
return render_forbidden unless current_user.admin? || observed_logged_user?
end
end

View File

@ -0,0 +1,83 @@
class Traces::ProjectsController < Traces::BaseController
include OperateProjectAbilityAble
before_action :require_login
before_action :load_project
before_action :authorizate_user_can_edit_project!, except: [:task_results]
def tasks
branch_name = params[:branch_name]
return render_error("无可用检测次数") if @project.user_trace_tasks.size >= 5
return render_error("分支名不能为空!") if branch_name.blank?
@all_branches = Gitea::Repository::Branches::ListNameService.call(@project&.owner, @project.identifier)
return render_error("请输入正确的分支名!") unless @all_branches["branch_name"].include?(branch_name)
code, data, error = Trace::CheckService.call(current_user.trace_token, @project, "1", branch_name)
if code == 200
UserTraceTask.create!(
user_id: current_user.id,
project_id: @project.id,
branch_tag: branch_name,
task_id: data["task_id"]
)
render_ok
else
render_error("检测失败 Error:#{error}")
end
rescue Exception => exception
puts exception.message
normal_status(-1, exception.message)
end
def task_results
limit = params[:limit] || params[:per_page]
limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i
page = params[:page].to_i.zero? ? 1 : params[:page].to_i
return render :json => {left_tasks_count: 5, data: []} if current_user.trace_user.nil?
code, data, error = Trace::CheckResultService.call(current_user.trace_token, @project, nil, page, limit)
if code == 200
render :json => {left_tasks_count: 5 - @project.user_trace_tasks.size, data: data}
else
render_error("获取检测记录失败 Error:#{error}")
end
rescue Exception => exception
puts exception.message
normal_status(-1, exception.message)
end
def reload_task
return render_error("project_id错误") if params[:project_id].blank?
branch_name = params[:branch_name]
return render_error("分支名不能为空!") if branch_name.blank?
@all_branches = Gitea::Repository::Branches::ListNameService.call(@project&.owner, @project.identifier)
return render_error("请输入正确的分支名!") unless @all_branches["branch_name"].include?(branch_name)
code, data, error = Trace::ReloadCheckService.call(current_user.trace_token, params[:project_id])
if code == 200
UserTraceTask.create!(
user_id: current_user.id,
project_id: @project.id,
branch_tag: branch_name,
task_id: data["task_id"]
)
render_ok
else
render_error("重新检测失败 Error:#{error}")
end
rescue Exception => exception
puts exception.message
normal_status(-1, exception.message)
end
def task_pdf
return render_error("task_id错误") if params[:task_id].blank?
result = Trace::PdfReportService.call(current_user.trace_token, params[:task_id])
if result.is_a?(Hash) && result[:code] == 200
redirect_to result[:download_url]
else
render_error("下载报告失败!")
end
rescue Exception => exception
puts exception.message
normal_status(-1, exception.message)
end
end

View File

@ -0,0 +1,14 @@
class Traces::TraceUsersController < Traces::BaseController
before_action :require_login
def create
if current_user.trace_token.present?
render_ok
else
render_error(-1, "代码溯源用户初始化失败")
end
rescue Exception => exception
puts exception.message
normal_status(-1, exception.message)
end
end

View File

@ -16,6 +16,7 @@ includes:
- users - users
- projects - projects
- repositories - repositories
- traces
- pulls - pulls
- issues - issues
- organizations - organizations

View File

@ -280,7 +280,7 @@ repo |是| |string |项目标识identifier
### 返回字段说明 ### 返回字段说明
参数 | 类型 | 字段说明 参数 | 类型 | 字段说明
--------- | ----------- | ----------- --------- | ----------- | -----------
menu_name |string|导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置 menu_name |string|导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,wiki:维基,services:服务,activity:动态,setting:仓库设置
> 返回的JSON示例: > 返回的JSON示例:
@ -408,7 +408,7 @@ await octokit.request('POST /api/yystopf/ceshi/project_units')
### 请求参数 ### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明 参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ---------- --------- | ------- | ------- | -------- | ----------
|unit_types |是| |array | 项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑 | |unit_types |是| |array | 项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,wiki:维基,resources:资源库,services:服务 |
### 返回字段说明: ### 返回字段说明:

View File

@ -0,0 +1,217 @@
# Traces
## 代码溯源初始化
用户同意协议后请求的接口,创建代码溯源的账号
> 示例:
```shell
curl -X POST \
http://localhost:3000/api/traces/trace_users.json
```
```javascript
await octokit.request('POST /api/traces/trace_users.json')
```
### HTTP 请求
`POST api/traces/trace_users.json`
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>
## 代码分析结果列表
查询项目下代码分析的结果
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/traces/yystopf/many_branch/task_results.json
```
```javascript
await octokit.request('GET /api/traces/:owner/:repo/task_results.json')
```
### HTTP 请求
`GET api/traces/:owner/:repo/task_results.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner|是|否|string | 项目所有者标识|
repo|是 | 否|string | 项目标识 |
page |否| 1 | int | 页码 |
limit |否| 15 | int | 每页数量 |
### 返回字段说明(暂缺)
<!-- 参数 | 类型 | 字段说明
--------- | ----------- | -----------
total_count |int |总数 |
public_keys.id |int |ID|
public_keys.name |string|密钥标题|
public_keys.content |string|密钥内容|
public_keys.fingerprint |string|密钥标识|
public_keys.created_time |string|密钥创建时间| -->
> 返回的JSON示例:
```json
{
"data": [
{
"accuracy": "20",
"code_type": "C",
"depth": "2",
"detect_flag": "快速-组件级",
"detect_rule": "快速-组件级,2,20,,开源软件,50,10",
"detect_startdate": "2022-05-10 15:59:46 ",
"detect_status": "fail",
"detectflag": "快速-组件级",
"fail_reason": "Invalid package type",
"file_name": "many_branch.zip",
"license_process": "100",
"licenseparam": "开源软件",
"package_type": "",
"product_name": "84727546110",
"project_id": "6dbc3e42-5857-4ca4-a54d-58fd9dbf6dc5",
"sim_process": "100",
"similarity_process": "2",
"task_id": "15139171-091b-4316-98b1-6068970efa44",
"totalsize": 5,
"uid": "78",
"vuln_process": "",
"vulnlevel": ""
}
]
}
```
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>
## 新建分析
用户选择仓库分支进行代码分析的接口
> 示例:
```shell
curl -X POST \
http://localhost:3000/api/traces/yystopf/many_branch/tasks.json
```
```javascript
await octokit.request('POST /api/traces/:owner/:repo/tasks.json')
```
### HTTP 请求
`POST api/traces/:owner/:repo/tasks.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner |是 | 否 | string | 项目所有者标识 |
repo |是 | 否 | string | 项目标识 |
branch_name|是 | 否| string | 分支名称 |
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>
## 重新扫描
对代码分析结果进行再次分析
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/traces/yystopf/many_branch/reload_task.json
```
```javascript
await octokit.request('GET /api/traces/:owner/:repo/reload_task.json')
```
### HTTP 请求
`GET api/traces/:owner/:repo/reload_task.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner |是 | 否 | string | 项目所有者标识 |
repo |是 | 否 | string | 项目标识 |
project_id|是 | 否| string | 代码分析结果里的project_id |
branch_name|是 | 否| string | 分支名称 |
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>
## 下载报告
把代码分析的结果下载到本地
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/traces/yystopf/many_branch/task_pdf.json
```
```javascript
await octokit.request('GET /api/traces/:owner/:repo/task_pdf.json')
```
### HTTP 请求
`GET api/traces/:owner/:repo/task_pdf.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner |是 | 否 | string | 项目所有者标识 |
repo |是 | 否 | string | 项目标识 |
task_id|是 | 否| string | 代码分析结果里的task_id |
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>

View File

@ -1,169 +1,218 @@
module RepositoriesHelper module RepositoriesHelper
def render_permission(user, project) def render_permission(user, project)
return "Admin" if user&.admin? return "Admin" if user&.admin?
project.get_premission(user) project.get_premission(user)
end end
def render_decode64_content(str) def render_decode64_content(str)
return nil if str.blank? return nil if str.blank?
Base64.decode64(str).force_encoding("UTF-8").encode("UTF-8", invalid: :replace) Base64.decode64(str).force_encoding("UTF-8").encode("UTF-8", invalid: :replace)
end end
def download_type(str) def download_type(str)
default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2 mp4 mov wmv flv mpeg avi avchd webm mkv) default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2 mp4 mov wmv flv mpeg avi avchd webm mkv)
default_type.include?(str&.downcase) || str.blank? default_type.include?(str&.downcase) || str.blank?
end end
def image_type?(str) def image_type?(str)
default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd) default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd)
default_type.include?(str&.downcase) default_type.include?(str&.downcase)
end end
def is_readme?(type, str) def is_readme?(type, str)
return false if type != 'file' || str.blank? return false if type != 'file' || str.blank?
readme_types = ["readme.md", "readme", "readme_en.md", "readme_zh.md", "readme_en", "readme_zh"] readme_types = ["readme.md", "readme", "readme_en.md", "readme_zh.md", "readme_en", "readme_zh"]
readme_types.include?(str.to_s.downcase) readme_types.include?(str.to_s.downcase)
end end
def render_commit_author(author_json) def render_commit_author(author_json)
return nil if author_json.blank? || (author_json["id"].blank? && author_json['name'].blank?) return nil if author_json.blank? || (author_json["id"].blank? && author_json['name'].blank?)
if author_json["id"].present? if author_json["id"].present?
return find_user_by_gitea_uid author_json['id'] return find_user_by_gitea_uid author_json['id']
end end
if author_json["id"].nil? && (author_json["name"].present? && author_json["email"].present?) if author_json["id"].nil? && (author_json["name"].present? && author_json["email"].present?)
return find_user_by_login_and_mail(author_json['name'], author_json["email"]) return find_user_by_login_and_mail(author_json['name'], author_json["email"])
end end
end end
def render_cache_commit_author(author_json) def render_cache_commit_author(author_json)
if author_json["name"].present? && author_json["email"].present? if author_json["name"].present? && author_json["email"].present?
return find_user_in_redis_cache(author_json['name'], author_json['email']) return find_user_in_redis_cache(author_json['name'], author_json['email'])
end end
if author_json["Name"].present? && author_json["Email"].present? if author_json["Name"].present? && author_json["Email"].present?
return find_user_in_redis_cache(author_json['Name'], author_json['Email']) return find_user_in_redis_cache(author_json['Name'], author_json['Email'])
end end
end end
def readme_render_decode64_content(str, owner, repo, ref, path) def readme_render_decode64_content(str, owner, repo, ref, path)
return nil if str.blank? return nil if str.blank?
begin begin
content = Base64.decode64(str).force_encoding('UTF-8') content = Base64.decode64(str).force_encoding('UTF-8')
c_regex = /\!\[.*?\]\((.*?)\)/ c_regex = /\!\[.*?\]\((.*?)\)/
src_regex = /src=\"(.*?)\"/ src_regex = /src=\"(.*?)\"/
src2_regex = /src='(.*?)'/ src2_regex = /src='(.*?)'/
ss = content.to_s.scan(c_regex) ss = content.to_s.scan(c_regex)
ss_src = content.scan(src_regex) ss_src = content.scan(src_regex)
ss_src2 = content.scan(src2_regex) ss_src2 = content.scan(src2_regex)
total_images = ss + ss_src + ss_src2 total_images = ss + ss_src + ss_src2
if total_images.length > 0 if total_images.length > 0
total_images.each do |s| total_images.each do |s|
begin begin
image_title = /\"(.*?)\"/ image_title = /\"(.*?)\"/
r_content = s[0] r_content = s[0]
remove_title = r_content.to_s.scan(image_title) remove_title = r_content.to_s.scan(image_title)
# if remove_title.length > 0 # if remove_title.length > 0
# r_content = r_content.gsub(/#{remove_title[0]}/, "").strip # r_content = r_content.gsub(/#{remove_title[0]}/, "").strip
# end # end
path_last = r_content path_last = r_content
path_current = "" path_current = ""
# 相对路径处理 # 相对路径处理
if r_content.start_with?("../") if r_content.start_with?("../")
relative_path_length = r_content.split("../").size - 1 relative_path_length = r_content.split("../").size - 1
path_pre = path.split("/").size - 1 - relative_path_length path_pre = path.split("/").size - 1 - relative_path_length
path_pre = 0 if path_pre < 0 path_pre = 0 if path_pre < 0
path_current = path_pre == 0 ? "" : path.split("/")[0..path_pre].join("/") path_current = path_pre == 0 ? "" : path.split("/")[0..path_pre].join("/")
path_last = r_content.split("../").last path_last = r_content.split("../").last
elsif r_content.start_with?("/") # 根路径处理 elsif r_content.start_with?("/") # 根路径处理
path_last = r_content[1..r_content.size] path_last = r_content[1..r_content.size]
else else
path_current = path path_current = path
end end
# if r_content.include?("?") # if r_content.include?("?")
# new_r_content = r_content + "&raw=true" # new_r_content = r_content + "&raw=true"
# else # else
# new_r_content = r_content + "?raw=true" # new_r_content = r_content + "?raw=true"
# end # end
new_r_content = r_content new_r_content = r_content
unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:") unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:")
# new_r_content = "#{path}" + new_r_content # new_r_content = "#{path}" + new_r_content
new_r_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{path_current}/#{path_last}&ref=#{ref}"].join new_r_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{path_current}/#{path_last}&ref=#{ref}"].join
end end
content = content.gsub(/src=\"#{r_content}\"/, "src=\"#{new_r_content}\"").gsub(/src='#{r_content}'/, "src=\"#{new_r_content}\"") content = content.gsub(/src=\"#{r_content}\"/, "src=\"#{new_r_content}\"").gsub(/src='#{r_content}'/, "src=\"#{new_r_content}\"")
rescue rescue
next next
end end
end end
end end
return content return content
rescue rescue
return str return str
end end
end end
# unix_time values for example: 1604382982 # author hui.he
def render_format_time_with_unix(unix_time) def new_readme_render_decode64_content(str, owner, repo, ref, readme_path, readme_name)
Time.at(unix_time).strftime("%Y-%m-%d %H:%M") file_path = readme_path.include?('/') ? readme_path.gsub("/#{readme_name}", '') : readme_path.gsub("#{readme_name}", '')
end return nil if str.blank?
content = Base64.decode64(str).force_encoding('UTF-8')
# date for example: 2020-11-01T19:57:27+08:00 s_regex = /\[.*?\]\((.*?)\)/
def render_format_time_with_date(date) src_regex = /src=\"(.*?)\"/
date.to_time.strftime("%Y-%m-%d %H:%M") ss = content.to_s.scan(s_regex)
end ss_src = content.to_s.scan(src_regex)
total_sources = ss + ss_src
def decode64_content(entry, owner, repo, ref, path=nil) total_sources.uniq!
if is_readme?(entry['type'], entry['name']) total_sources.each do |s|
# content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] begin
content = entry['content'] s_content = s[0]
path = URI.escape(entry['path']).to_s.downcase.gsub("/readme.md","") # 链接直接跳过不做替换
readme_render_decode64_content(content, owner, repo, ref, path) next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:')
else ext = File.extname(s_content)[1..-1]
file_type = File.extname(entry['name'].to_s)[1..-1]
if image_type?(file_type) if image_type?(ext) || download_type(ext)
return entry['content'].nil? ? Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] : entry['content'] s_content = File.expand_path(s_content, file_path)
end s_content = s_content.split("#{Rails.root}/")[1]
if download_type(file_type) # content = content.gsub(s[0], "/#{s_content}")
return entry['content'] s_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{s_content}&ref=#{ref}"].join
end content = content.gsub(s[0], s_content)
render_decode64_content(entry['content']) else
end path = [owner&.login, repo&.identifier, 'tree', ref, file_path].join("/")
end s_content = File.expand_path(s_content, path)
s_content = s_content.split("#{Rails.root}/")[1]
def base64_to_image(path, content) content = content.gsub(s[0], "/#{s_content}")
# generate to https://git.trusite.net/pawm36ozq/-/raw/branch/master/entrn.png" end
content = Base64.decode64(content) rescue
File.open(path, 'wb') { |f| f.write(content) } next
end end
end
def render_download_image_url(dir_path, file_path, content)
full_path = file_path.starts_with?("/") ? [dir_path, file_path].join("") : [dir_path, file_path].join("/") return content
file_name = full_path.split("/")[-1] rescue
# 用户名/项目标识/文件路径 return str
dir_path = generate_dir_path(full_path.split("/"+file_name)[0]) end
file_path = [dir_path, file_name].join('/') # unix_time values for example: 1604382982
def render_format_time_with_unix(unix_time)
puts "##### render_download_image_url file_path: #{file_path}" Time.at(unix_time).strftime("%Y-%m-%d %H:%M")
base64_to_image(file_path, content) end
file_path = file_path[6..-1]
File.join(base_url, file_path) # date for example: 2020-11-01T19:57:27+08:00
end def render_format_time_with_date(date)
date.to_time.strftime("%Y-%m-%d %H:%M")
def generate_dir_path(dir_path) end
# tmp_dir_path
# eg: jasder/forgeplus/raw/branch/ref def readme_decode64_content(entry, owner, repo, ref, path=nil)
dir_path = ["public", tmp_dir, dir_path].join('/') Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}")
puts "#### dir_path: #{dir_path}" content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
unless Dir.exists?(dir_path) Rails.logger.info("content===#{content}")
FileUtils.mkdir_p(dir_path) ##不成功这里会抛异常 # readme_render_decode64_content(content, owner, repo, ref)
end new_readme_render_decode64_content(content, owner, repo, ref, entry['path'], entry['name'])
dir_path end
end
def decode64_content(entry, owner, repo, ref, path=nil)
def tmp_dir if is_readme?(entry['type'], entry['name'])
"repo" Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}")
end content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
Rails.logger.info("content===#{content}")
end # readme_render_decode64_content(content, owner, repo, ref)
return Base64.decode64(content).force_encoding('UTF-8')
else
file_type = File.extname(entry['name'].to_s)[1..-1]
if image_type?(file_type)
return entry['content'].nil? ? Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] : entry['content']
end
if download_type(file_type)
return entry['content']
end
render_decode64_content(entry['content'])
end
end
def base64_to_image(path, content)
# generate to https://git.trusite.net/pawm36ozq/-/raw/branch/master/entrn.png"
content = Base64.decode64(content)
File.open(path, 'wb') { |f| f.write(content) }
end
def render_download_image_url(dir_path, file_path, content)
full_path = file_path.starts_with?("/") ? [dir_path, file_path].join("") : [dir_path, file_path].join("/")
file_name = full_path.split("/")[-1]
# 用户名/项目标识/文件路径
dir_path = generate_dir_path(full_path.split("/"+file_name)[0])
file_path = [dir_path, file_name].join('/')
puts "##### render_download_image_url file_path: #{file_path}"
base64_to_image(file_path, content)
file_path = file_path[6..-1]
File.join(base_url, file_path)
end
def generate_dir_path(dir_path)
# tmp_dir_path
# eg: jasder/forgeplus/raw/branch/ref
dir_path = ["public", tmp_dir, dir_path].join('/')
puts "#### dir_path: #{dir_path}"
unless Dir.exists?(dir_path)
FileUtils.mkdir_p(dir_path) ##不成功这里会抛异常
end
dir_path
end
def tmp_dir
"repo"
end
end

View File

@ -0,0 +1,15 @@
class Admins::NewImportUserFromExcel < BaseImportXlsx
UserData = Struct.new(:login, :email, :password, :nickname)
def read_each(&block)
sheet.each_row_streaming(pad_cells: true, offset: 1) do |row|
data = row.map(&method(:cell_value))[0..3]
block.call UserData.new(*data)
end
end
private
def cell_value(obj)
obj&.cell_value
end
end

View File

@ -1,7 +1,7 @@
module CustomRegexp module CustomRegexp
PHONE = /1\d{10}/ PHONE = /1\d{10}/
EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/ EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/
LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]{4,15}/ #只含有数字、字母、下划线不能以下划线开头和结尾
LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/ LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/
NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/ NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/
PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/ PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/

View File

@ -141,6 +141,7 @@ class MessageTemplate::ProjectSettingChanged < MessageTemplate
navbar.gsub!('devops', '工作流') navbar.gsub!('devops', '工作流')
navbar.gsub!('versions', '里程碑') navbar.gsub!('versions', '里程碑')
navbar.gsub!('resources', '资源库') navbar.gsub!('resources', '资源库')
navbar.gsub!('services', '服务')
if change_count > 1 if change_count > 1
content.sub!('{ifnavbar}', '<br/>') content.sub!('{ifnavbar}', '<br/>')
else else
@ -290,6 +291,7 @@ class MessageTemplate::ProjectSettingChanged < MessageTemplate
navbar.gsub!('devops', '工作流') navbar.gsub!('devops', '工作流')
navbar.gsub!('versions', '里程碑') navbar.gsub!('versions', '里程碑')
navbar.gsub!('resources', '资源库') navbar.gsub!('resources', '资源库')
navbar.gsub!('services', '服务')
if change_count > 1 if change_count > 1
content.sub!('{ifnavbar}', '<br/>') content.sub!('{ifnavbar}', '<br/>')
else else

View File

@ -124,6 +124,7 @@ class Project < ApplicationRecord
has_many :pinned_projects, dependent: :destroy has_many :pinned_projects, dependent: :destroy
has_many :has_pinned_users, through: :pinned_projects, source: :user has_many :has_pinned_users, through: :pinned_projects, source: :user
has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id
has_many :user_trace_tasks, dependent: :destroy
after_create :incre_user_statistic, :incre_platform_statistic after_create :incre_user_statistic, :incre_platform_statistic
after_save :check_project_members after_save :check_project_members
before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data

View File

@ -16,7 +16,7 @@
class ProjectUnit < ApplicationRecord class ProjectUnit < ApplicationRecord
belongs_to :project belongs_to :project
enum unit_type: {code: 1, issues: 2, pulls: 3, wiki:4, devops: 5, versions: 6, resources: 7} enum unit_type: {code: 1, issues: 2, pulls: 3, wiki:4, devops: 5, versions: 6, resources: 7, services: 8}
validates :unit_type, uniqueness: { scope: :project_id} validates :unit_type, uniqueness: { scope: :project_id}

View File

@ -175,6 +175,7 @@ class User < Owner
has_many :system_notification_histories has_many :system_notification_histories
has_many :system_notifications, through: :system_notification_histories has_many :system_notifications, through: :system_notification_histories
has_one :trace_user, dependent: :destroy has_one :trace_user, dependent: :destroy
has_many :user_trace_tasks, dependent: :destroy
# Groups and active users # Groups and active users
scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) } scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) }

View File

@ -0,0 +1,25 @@
# == Schema Information
#
# Table name: user_trace_tasks
#
# id :integer not null, primary key
# user_id :integer
# project_id :integer
# branch_tag :string(255)
# task_id :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_user_trace_tasks_on_project_id (project_id)
# index_user_trace_tasks_on_user_id (user_id)
#
class UserTraceTask < ApplicationRecord
belongs_to :user
belongs_to :project
end

View File

@ -0,0 +1,71 @@
class Admins::ImportUserFromExcelService < ApplicationService
Error = Class.new(StandardError)
attr_reader :file, :result
def initialize(file)
@file = file
@result = { success: 0, fail: [] }
end
def call
raise Error, '文件不存在' if file.blank?
excel = Admins::NewImportUserFromExcel.new(file)
excel.read_each(&method(:save_user))
result
rescue ApplicationImport::Error => ex
raise Error, ex.message
end
private
def save_user(data)
user = find_user(data)
if user.blank?
create_user(data)
result[:success] +=1
else
fail_data = data.as_json
fail_data[:data] = fail_data.values.join(",")
fail_data[:message] = '用户已存在'
result[:fail] << fail_data
end
rescue Exception => ex
fail_data = data.as_json
fail_data[:data] = fail_data.values.join(",")
fail_data[:message] = ex.message
result[:fail] << fail_data
end
def create_user(data)
ActiveRecord::Base.transaction do
username = data.login&.gsub(/\s+/, "")
email = data.email&.gsub(/\s+/, "")
password = data.password
nickname = data.nickname&.gsub(/\s+/, "")
raise Error, "无法使用以下关键词:#{username},请重新命名" if ReversedKeyword.check_exists?(data.login)
Register::RemoteForm.new({username: username, email: email, password: password, platform: 'forge'}).validate!
user = User.new(admin: false, login: username, mail: email, nickname: nickname, platform: 'forge' , type: "User")
user.password = password
user.activate
raise Error, user.errors.full_messages.join(",") unless user.valid?
interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password})
if interactor.success?
gitea_user = interactor.result
result = Gitea::User::GenerateTokenService.call(username, password)
user.gitea_token = result['sha1']
user.gitea_uid = gitea_user[:body]['id']
UserExtension.create!(user_id: user.id) if user.save!
else
raise interactor.error, 'gitea user create error'
end
user
end
end
def find_user(data)
User.find_by(login: data.login)
end
end

View File

@ -1,11 +1,11 @@
# 代码溯源 查询检测结果 # 代码溯源 查询检测结果
class Trace::CheckResultService < Trace::ClientService class Trace::CheckResultService < Trace::ClientService
attr_accessor :token, :project_name, :file_name, :page_num, :page_size attr_accessor :token, :project, :file_name, :page_num, :page_size
def initialize(token, project_name=nil, file_name=nil, page_num=1, page_size=15) def initialize(token, project, file_name=nil, page_num=1, page_size=15)
@token = token @token = token
@project_name = project_name @project = project
@file_name = file_name @file_name = file_name
@page_num = page_num @page_num = page_num
@page_size = page_size @page_size = page_size
@ -19,7 +19,7 @@ class Trace::CheckResultService < Trace::ClientService
private private
def request_params def request_params
{ {
product_name: project_name, product_name: Digest::MD5.hexdigest(project&.id.to_s)[0...20],
file_name: file_name, file_name: file_name,
pageNum: page_num, pageNum: page_num,
pageSize: page_size, pageSize: page_size,

View File

@ -11,26 +11,25 @@ class Trace::CheckService < Trace::ClientService
end end
def call def call
result = authed_post(token, url, {data: request_params}) result = http_authed_post(token, url, {data: request_params})
reponse = render_response(result) reponse = render_response(result)
end end
private private
def request_params def request_params
repo = Gitea::Repository::GetService.call(project&.owner&.login, project&.identifier) repo = Gitea::Repository::GetService.call(project&.owner, project&.identifier)
{ {
product_name: project&.name, product_name: Digest::MD5.hexdigest(project&.id.to_s)[0...20],
product_type: project&.category&.name, product_type: project&.project_category&.name || '其他',
code_type: project&.language&.name, code_type: project&.project_language&.name || '其他',
product_desc: project&.description, product_desc: project&.description,
git_url: repo['clone_url'], git_url: repo['clone_url'],
if_branch: if_branch, if_branch: if_branch,
branch_tag: branch_tag branch_tag: branch_tag
} }.compact
end end
def url def url
"/user/check".freeze "/user/check".freeze
end end
end end

View File

@ -12,6 +12,19 @@ class Trace::ClientService < ApplicationService
conn.post(full_url(url), params[:data]) conn.post(full_url(url), params[:data])
end end
def http_authed_post(token, url, params={})
puts "[trace][POST] request params: #{params}"
puts "[trace][POST] request token: #{token}"
url = URI("#{full_url(url)}")
http = Net::HTTP.new(url.host, url.port)
http.read_timeout = 1200
request = Net::HTTP::Post.new(url)
request["Authorization"] = token
form_data = params[:data].stringify_keys.to_a
request.set_form form_data, 'multipart/form-data'
http.request(request)
end
def get(url, params={}) def get(url, params={})
puts "[trace][GET] request params: #{params}" puts "[trace][GET] request params: #{params}"
conn.get do |req| conn.get do |req|
@ -100,11 +113,22 @@ class Trace::ClientService < ApplicationService
end end
def render_response(response) def render_response(response)
status = response.status if response.is_a?(Faraday::Response)
body = JSON.parse(response&.body) status = response.status
body = JSON.parse(response&.body)
log_error(status, body) log_error(status, body)
return [body["code"], body["data"], body["error"]] return [body["code"], body["data"], body["error"]]
end
if response.is_a?(Net::HTTPOK)
status = 200
body = JSON.parse(response&.body)
log_error(status, body)
return [body["code"], body["data"], body["error"]]
end
end end
end end

View File

@ -1,4 +1,7 @@
# 代码溯源 导出pdf # 代码溯源 导出pdf
require 'open-uri'
require 'fileutils'
class Trace::PdfReportService < Trace::ClientService class Trace::PdfReportService < Trace::ClientService
attr_accessor :token, :task_id attr_accessor :token, :task_id
@ -9,15 +12,23 @@ class Trace::PdfReportService < Trace::ClientService
end end
def call def call
result = authed_get(token, url, request_params) content = open("#{domain}#{base_url}#{url}?task_id=#{task_id}", "Authorization" => token)
response = render_response(result) if content.is_a?(Tempfile)
check_file_path
IO.copy_stream(content, "#{save_path}/#{task_id}.pdf")
return {code: 200, download_url: "/trace_task_results/#{task_id}.pdf"}
else
return {code: 404}
end
end end
private private
def request_params def check_file_path
{ FileUtils.mkdir_p save_path
task_id: task_id end
}
def save_path
"public/trace_task_results"
end end
def url def url

View File

@ -31,7 +31,9 @@
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
<% end %> <% end %>
<%= javascript_void_link '导入用户', class: 'btn btn-secondary btn-sm', data: { toggle: 'modal', target: '.admin-import-user-modal'} %> <%= link_to '下载导入模板', "/导入用户模板.xlsx", class: 'btn btn-secondary mr-3' %>
<%= javascript_void_link '导入用户', class: 'btn btn-secondary', data: { toggle: 'modal', target: '.admin-import-user-modal'} %>
</div> </div>

View File

@ -9,7 +9,10 @@ if @project.forge?
json.path entry['path'] json.path entry['path']
json.type entry['type'] json.type entry['type']
json.size entry['size'] json.size entry['size']
is_readme = is_readme?(entry['type'], entry['name'])
if is_readme
json.replace_content readme_decode64_content(entry, @owner, @repository, @ref, @path)
end
json.content (direct_download || image_type || is_dir) ? nil : decode64_content(entry, @owner, @repository, @ref, @path) json.content (direct_download || image_type || is_dir) ? nil : decode64_content(entry, @owner, @repository, @ref, @path)
json.target entry['target'] json.target entry['target']
@ -25,7 +28,7 @@ if @project.forge?
json.direct_download direct_download json.direct_download direct_download
json.image_type image_type json.image_type image_type
json.is_readme_file is_readme?(entry['type'], entry['name']) json.is_readme_file is_readme
json.commit do json.commit do
json.partial! 'last_commit', latest_commit: entry['latest_commit'] json.partial! 'last_commit', latest_commit: entry['latest_commit']
end end

View File

@ -25,3 +25,4 @@ json.description @user.description
json.super_description @user.super_description json.super_description @user.super_description
json.(@user, :show_email, :show_department, :show_location, :show_super_description) json.(@user, :show_email, :show_department, :show_location, :show_super_description)
json.message_unread_total @message_unread_total json.message_unread_total @message_unread_total
json.has_trace_user @user.trace_user.present?

View File

@ -427,6 +427,20 @@ Rails.application.routes.draw do
end end
end end
namespace :traces do
resources :trace_users, only: [:create]
scope "/:owner/:repo" do
resource :projects, path: '/', only: [:index] do
member do
post :tasks
get :task_results
get :reload_task
get :task_pdf
end
end
end
end
# Project Area START # Project Area START
scope "/:owner/:repo" do scope "/:owner/:repo" do
scope do scope do

View File

@ -0,0 +1,12 @@
class CreateUserTraceTasks < ActiveRecord::Migration[5.2]
def change
create_table :user_trace_tasks do |t|
t.references :user
t.references :project
t.string :branch_tag
t.string :task_id
t.timestamps
end
end
end

View File

@ -543,6 +543,26 @@
</li> </li>
</ul> </ul>
</li> </li>
<li>
<a href="#traces" class="toc-h1 toc-link" data-title="Traces">Traces</a>
<ul class="toc-list-h2">
<li>
<a href="#ca438fc3ca" class="toc-h2 toc-link" data-title="代码溯源初始化">代码溯源初始化</a>
</li>
<li>
<a href="#bb16c601f1" class="toc-h2 toc-link" data-title="代码分析结果列表">代码分析结果列表</a>
</li>
<li>
<a href="#32497859e0" class="toc-h2 toc-link" data-title="新建分析">新建分析</a>
</li>
<li>
<a href="#7b3a48e274" class="toc-h2 toc-link" data-title="重新扫描">重新扫描</a>
</li>
<li>
<a href="#87775b1430" class="toc-h2 toc-link" data-title="下载报告">下载报告</a>
</li>
</ul>
</li>
<li> <li>
<a href="#pulls" class="toc-h1 toc-link" data-title="Pulls">Pulls</a> <a href="#pulls" class="toc-h1 toc-link" data-title="Pulls">Pulls</a>
<ul class="toc-list-h2"> <ul class="toc-list-h2">
@ -4968,7 +4988,7 @@ http://localhost:3000/api/yystopf/ceshi/menu_list | jq
<tr> <tr>
<td>menu_name</td> <td>menu_name</td>
<td>string</td> <td>string</td>
<td>导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置</td> <td>导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,wiki:维基,services:服务,activity:动态,setting:仓库设置</td>
</tr> </tr>
</tbody></table> </tbody></table>
@ -5131,7 +5151,7 @@ http://localhost:3000/api/yystopf/ceshi/project_units.json
<td></td> <td></td>
<td></td> <td></td>
<td>array</td> <td>array</td>
<td>项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑</td> <td>项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,wiki:维基,resources:资源库,services:服务</td>
</tr> </tr>
</tbody></table> </tbody></table>
<h3 id='7447e4874e-2'>返回字段说明:</h3> <h3 id='7447e4874e-2'>返回字段说明:</h3>
@ -9302,6 +9322,289 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/3/test.json
<aside class="success"> <aside class="success">
Success Data. Success Data.
</aside> </aside>
<h1 id='traces'>Traces</h1><h2 id='ca438fc3ca'>代码溯源初始化</h2>
<p>用户同意协议后请求的接口,创建代码溯源的账号</p>
<blockquote>
<p>示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> POST <span class="se">\</span>
http://localhost:3000/api/traces/trace_users.json
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST /api/traces/trace_users.json</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div><h3 id='http'>HTTP 请求</h3>
<p><code>POST api/traces/trace_users.json</code></p>
<blockquote>
<p>返回的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>
<h2 id='bb16c601f1'>代码分析结果列表</h2>
<p>查询项目下代码分析的结果</p>
<blockquote>
<p>示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> GET <span class="se">\</span>
http://localhost:3000/api/traces/yystopf/many_branch/task_results.json
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET /api/traces/:owner/:repo/task_results.json</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div><h3 id='http-2'>HTTP 请求</h3>
<p><code>GET api/traces/:owner/:repo/task_results.json</code></p>
<h3 id='1f9ac54b15'>请求参数</h3>
<table><thead>
<tr>
<th>参数</th>
<th>必选</th>
<th>默认</th>
<th>类型</th>
<th>字段说明</th>
</tr>
</thead><tbody>
<tr>
<td>owner</td>
<td></td>
<td></td>
<td>string</td>
<td>项目所有者标识</td>
</tr>
<tr>
<td>repo</td>
<td></td>
<td></td>
<td>string</td>
<td>项目标识</td>
</tr>
<tr>
<td>page</td>
<td></td>
<td>1</td>
<td>int</td>
<td>页码</td>
</tr>
<tr>
<td>limit</td>
<td></td>
<td>15</td>
<td>int</td>
<td>每页数量</td>
</tr>
</tbody></table>
<h3 id='90889036d2'>返回字段说明(暂缺)</h3>
<!-- 参数 | 类型 | 字段说明
--------- | ----------- | -----------
total_count |int |总数 |
public_keys.id |int |ID|
public_keys.name |string|密钥标题|
public_keys.content |string|密钥内容|
public_keys.fingerprint |string|密钥标识|
public_keys.created_time |string|密钥创建时间| -->
<blockquote>
<p>返回的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"accuracy"</span><span class="p">:</span><span class="w"> </span><span class="s2">"20"</span><span class="p">,</span><span class="w">
</span><span class="nl">"code_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"C"</span><span class="p">,</span><span class="w">
</span><span class="nl">"depth"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2"</span><span class="p">,</span><span class="w">
</span><span class="nl">"detect_flag"</span><span class="p">:</span><span class="w"> </span><span class="s2">"快速-组件级"</span><span class="p">,</span><span class="w">
</span><span class="nl">"detect_rule"</span><span class="p">:</span><span class="w"> </span><span class="s2">"快速-组件级,2,20,,开源软件,50,10"</span><span class="p">,</span><span class="w">
</span><span class="nl">"detect_startdate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-05-10 15:59:46 "</span><span class="p">,</span><span class="w">
</span><span class="nl">"detect_status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fail"</span><span class="p">,</span><span class="w">
</span><span class="nl">"detectflag"</span><span class="p">:</span><span class="w"> </span><span class="s2">"快速-组件级"</span><span class="p">,</span><span class="w">
</span><span class="nl">"fail_reason"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Invalid package type"</span><span class="p">,</span><span class="w">
</span><span class="nl">"file_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"many_branch.zip"</span><span class="p">,</span><span class="w">
</span><span class="nl">"license_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">"100"</span><span class="p">,</span><span class="w">
</span><span class="nl">"licenseparam"</span><span class="p">:</span><span class="w"> </span><span class="s2">"开源软件"</span><span class="p">,</span><span class="w">
</span><span class="nl">"package_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
</span><span class="nl">"product_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"84727546110"</span><span class="p">,</span><span class="w">
</span><span class="nl">"project_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6dbc3e42-5857-4ca4-a54d-58fd9dbf6dc5"</span><span class="p">,</span><span class="w">
</span><span class="nl">"sim_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">"100"</span><span class="p">,</span><span class="w">
</span><span class="nl">"similarity_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2"</span><span class="p">,</span><span class="w">
</span><span class="nl">"task_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"15139171-091b-4316-98b1-6068970efa44"</span><span class="p">,</span><span class="w">
</span><span class="nl">"totalsize"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w">
</span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"78"</span><span class="p">,</span><span class="w">
</span><span class="nl">"vuln_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
</span><span class="nl">"vulnlevel"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>
<h2 id='32497859e0'>新建分析</h2>
<p>用户选择仓库分支进行代码分析的接口</p>
<blockquote>
<p>示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> POST <span class="se">\</span>
http://localhost:3000/api/traces/yystopf/many_branch/tasks.json
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST /api/traces/:owner/:repo/tasks.json</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div><h3 id='http-3'>HTTP 请求</h3>
<p><code>POST api/traces/:owner/:repo/tasks.json</code></p>
<h3 id='1f9ac54b15-2'>请求参数</h3>
<table><thead>
<tr>
<th>参数</th>
<th>必选</th>
<th>默认</th>
<th>类型</th>
<th>字段说明</th>
</tr>
</thead><tbody>
<tr>
<td>owner</td>
<td></td>
<td></td>
<td>string</td>
<td>项目所有者标识</td>
</tr>
<tr>
<td>repo</td>
<td></td>
<td></td>
<td>string</td>
<td>项目标识</td>
</tr>
<tr>
<td>branch_name</td>
<td></td>
<td></td>
<td>string</td>
<td>分支名称</td>
</tr>
</tbody></table>
<blockquote>
<p>返回的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>
<h2 id='7b3a48e274'>重新扫描</h2>
<p>对代码分析结果进行再次分析</p>
<blockquote>
<p>示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> GET <span class="se">\</span>
http://localhost:3000/api/traces/yystopf/many_branch/reload_task.json
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET /api/traces/:owner/:repo/reload_task.json</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div><h3 id='http-4'>HTTP 请求</h3>
<p><code>GET api/traces/:owner/:repo/reload_task.json</code></p>
<h3 id='1f9ac54b15-3'>请求参数</h3>
<table><thead>
<tr>
<th>参数</th>
<th>必选</th>
<th>默认</th>
<th>类型</th>
<th>字段说明</th>
</tr>
</thead><tbody>
<tr>
<td>owner</td>
<td></td>
<td></td>
<td>string</td>
<td>项目所有者标识</td>
</tr>
<tr>
<td>repo</td>
<td></td>
<td></td>
<td>string</td>
<td>项目标识</td>
</tr>
<tr>
<td>project_id</td>
<td></td>
<td></td>
<td>string</td>
<td>代码分析结果里的project_id</td>
</tr>
</tbody></table>
<blockquote>
<p>返回的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>
<h2 id='87775b1430'>下载报告</h2>
<p>把代码分析的结果下载到本地</p>
<blockquote>
<p>示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> GET <span class="se">\</span>
http://localhost:3000/api/traces/yystopf/many_branch/task_pdf.json
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET /api/traces/:owner/:repo/task_pdf.json</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div><h3 id='http-5'>HTTP 请求</h3>
<p><code>GET api/traces/:owner/:repo/task_pdf.json</code></p>
<h3 id='1f9ac54b15-4'>请求参数</h3>
<table><thead>
<tr>
<th>参数</th>
<th>必选</th>
<th>默认</th>
<th>类型</th>
<th>字段说明</th>
</tr>
</thead><tbody>
<tr>
<td>owner</td>
<td></td>
<td></td>
<td>string</td>
<td>项目所有者标识</td>
</tr>
<tr>
<td>repo</td>
<td></td>
<td></td>
<td>string</td>
<td>项目标识</td>
</tr>
<tr>
<td>task_id</td>
<td></td>
<td></td>
<td>string</td>
<td>代码分析结果里的task_id</td>
</tr>
</tbody></table>
<blockquote>
<p>返回的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>
<h1 id='pulls'>Pulls</h1><h2 id='get-a-pull-request'>Get a pull request</h2> <h1 id='pulls'>Pulls</h1><h2 id='get-a-pull-request'>Get a pull request</h2>
<p>获取合并请求详情接口</p> <p>获取合并请求详情接口</p>

Binary file not shown.

View File

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe UserTraceTask, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end