Merge pull request '20220722版本' (#309) from Trustie/forgeplus:trustie_server into master

This commit is contained in:
xxq250 2022-07-22 19:57:44 +08:00
commit 802568cf89
126 changed files with 8821 additions and 1939 deletions

268
Gemfile
View File

@ -1,130 +1,138 @@
source 'https://gems.ruby-china.com'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem 'rails', '~> 5.2.0'
gem 'mysql2', '>= 0.4.4', '< 0.6.0'
gem 'puma', '~> 3.11'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
# gem 'coffee-rails', '~> 4.2'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.5'
gem 'groupdate', '~> 4.1.0'
gem 'chartkick'
gem 'grape-entity', '~> 0.7.1'
gem 'kaminari', '~> 1.1', '>= 1.1.1'
gem 'bootsnap', '>= 1.1.0', require: false
gem 'chinese_pinyin'
gem 'rack-cors'
gem 'redis-rails'
gem 'roo-xls'
gem 'simple_xlsx_reader'
gem 'rubyzip'
gem 'spreadsheet'
gem 'ruby-ole'
# 导出为xlsx
gem 'axlsx', '~> 3.0.0.pre'
gem 'axlsx_rails', '~> 0.5.2'
gem 'oauth2'
#导出为pdf
gem 'pdfkit'
gem 'wkhtmltopdf-binary'
# gem 'request_store'
#gem 'iconv'
# markdown 转html
gem 'redcarpet', '~> 3.4'
gem 'rqrcode', '~> 0.10.1'
gem 'rqrcode_png'
gem 'acts-as-taggable-on', '~> 6.0'
# a tree structure
gem 'ancestry'
gem 'acts_as_list'
gem 'omniauth-cas'
# profiler Middleware
gem 'rack-mini-profiler'
# object-based searching
gem 'ransack'
group :development, :test do
gem 'rspec-rails', '~> 3.8'
end
group :development do
gem 'prettier'
gem 'rubocop', '~> 0.52.0'
gem 'solargraph', '~> 0.38.0'
gem 'awesome_print'
gem 'web-console', '>= 3.3.0'
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
gem 'capybara', '>= 2.15', '< 4.0'
gem 'selenium-webdriver'
gem 'chromedriver-helper'
end
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
#编码检测
gem 'rchardet', '~> 1.8'
# http client
gem 'faraday', '~> 0.15.4'
# view
gem 'active_decorator'
gem 'bootstrap', '~> 4.3.1'
gem 'jquery-rails'
gem 'simple_form'
gem 'font-awesome-sass', '4.7.0'
# i18n
gem 'rails-i18n', '~> 5.1'
# job
gem 'sidekiq'
gem 'sinatra'
gem "sidekiq-cron", "~> 1.1"
# batch insert
gem 'bulk_insert'
# elasticsearch
gem 'searchkick'
gem 'aasm'
gem 'enumerize'
gem 'diffy'
gem 'deep_cloneable', '~> 3.0.0'
# oauth2
gem 'omniauth', '~> 1.9.0'
gem 'omniauth-oauth2', '~> 1.6.0'
# global var
gem 'request_store'
# 敏感词汇
gem 'harmonious_dictionary', '~> 0.0.1'
gem 'parallel', '~> 1.19', '>= 1.19.1'
gem 'letter_avatar'
source 'https://gems.ruby-china.com'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem 'rails', '~> 5.2.0'
gem 'mysql2', '>= 0.4.4', '< 0.6.0'
gem 'puma', '~> 3.11'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
# gem 'coffee-rails', '~> 4.2'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.5'
gem 'groupdate', '~> 4.1.0'
gem 'chartkick'
gem 'grape-entity', '~> 0.7.1'
gem 'kaminari', '~> 1.1', '>= 1.1.1'
gem 'bootsnap', '>= 1.1.0', require: false
gem 'chinese_pinyin'
gem 'rack-cors'
gem 'redis-rails'
gem 'roo-xls'
gem 'simple_xlsx_reader'
gem 'rubyzip'
gem 'spreadsheet'
gem 'ruby-ole'
# 导出为xlsx
gem 'axlsx', '~> 3.0.0.pre'
gem 'axlsx_rails', '~> 0.5.2'
gem 'oauth2'
#导出为pdf
gem 'pdfkit'
gem 'wkhtmltopdf-binary'
# gem 'request_store'
#gem 'iconv'
# markdown 转html
gem 'redcarpet', '~> 3.4'
gem 'rqrcode', '~> 0.10.1'
gem 'rqrcode_png'
gem 'acts-as-taggable-on', '~> 6.0'
# a tree structure
gem 'ancestry'
gem 'acts_as_list'
gem 'omniauth-cas'
# profiler Middleware
gem 'rack-mini-profiler'
# object-based searching
gem 'ransack'
group :development, :test do
gem 'rspec-rails', '~> 3.8'
end
group :development do
gem 'prettier'
gem 'rubocop', '~> 0.52.0'
gem 'solargraph', '~> 0.38.0'
gem 'awesome_print'
gem 'web-console', '>= 3.3.0'
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
gem 'capybara', '>= 2.15', '< 4.0'
gem 'selenium-webdriver'
gem 'chromedriver-helper'
end
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
#编码检测
gem 'rchardet', '~> 1.8'
# http client
gem 'faraday', '~> 0.15.4'
# view
gem 'active_decorator'
gem 'bootstrap', '~> 4.3.1'
gem 'jquery-rails'
gem 'simple_form'
gem 'font-awesome-sass', '4.7.0'
# i18n
gem 'rails-i18n', '~> 5.1'
# job
gem 'sidekiq'
gem 'sinatra'
gem "sidekiq-cron", "~> 1.1"
# batch insert
gem 'bulk_insert'
# elasticsearch
gem 'searchkick'
gem 'aasm'
gem 'enumerize'
gem 'diffy'
gem 'deep_cloneable', '~> 3.0.0'
# oauth2
gem 'omniauth', '~> 1.9.0'
gem 'omniauth-oauth2', '~> 1.6.0'
# global var
gem 'request_store'
# 敏感词汇
gem 'harmonious_dictionary', '~> 0.0.1'
gem 'parallel', '~> 1.19', '>= 1.19.1'
gem 'letter_avatar'
gem 'jwt'
gem 'doorkeeper'
gem 'doorkeeper-jwt'
gem 'gitea-client', '~> 0.10.2'

View File

@ -106,6 +106,10 @@ GEM
activerecord (>= 3.1.0, < 7)
diff-lcs (1.3)
diffy (3.3.0)
doorkeeper (5.5.1)
railties (>= 5)
doorkeeper-jwt (0.4.1)
jwt (>= 2.1)
e2mmap (0.1.0)
elasticsearch (7.5.0)
elasticsearch-api (= 7.5.0)
@ -450,6 +454,8 @@ DEPENDENCIES
chromedriver-helper
deep_cloneable (~> 3.0.0)
diffy
doorkeeper
doorkeeper-jwt
enumerize
faraday (~> 0.15.4)
font-awesome-sass (= 4.7.0)
@ -458,6 +464,7 @@ DEPENDENCIES
harmonious_dictionary (~> 0.0.1)
jbuilder (~> 2.5)
jquery-rails
jwt
kaminari (~> 1.1, >= 1.1.1)
letter_avatar
listen (>= 3.0.5, < 3.2)

View File

@ -1,10 +1,33 @@
class Admins::DashboardsController < Admins::BaseController
def index
@active_user_count = User.where(last_login_on: today).count
@weekly_active_user_count = User.where(last_login_on: current_week).count
@month_active_user_count = User.where(last_login_on: current_month).count
# 用户活跃数
day_user_ids = CommitLog.where(created_at: today).pluck(:project_id).uniq
weekly_user_ids = CommitLog.where(created_at: current_week).pluck(:project_id).uniq
month_user_ids = CommitLog.where(created_at: current_month).pluck(:project_id).uniq
@active_user_count = User.where(last_login_on: today).or(User.where(id: day_user_ids)).count
@weekly_active_user_count = User.where(last_login_on: current_week).or(User.where(id: weekly_user_ids)).count
@month_active_user_count = User.where(last_login_on: current_month).or(User.where(id: month_user_ids)).count
user_ids = User.where(created_on: pre_week).pluck(:id).uniq
weekly_keep_user_count = User.where(id: user_ids).where(last_login_on: current_week).count
@weekly_keep_rate = format("%.2f", user_ids.size > 0 ? weekly_keep_user_count.to_f / user_ids.size : 0)
@new_user_count = User.where(created_on: current_month).count
# 新用户注册数
@day_new_user_count = User.where(created_on: today).count
@weekly_new_user_count = User.where(created_on: current_week).count
@month_new_user_count = User.where(created_on: current_month).count
# 活跃项目数
day_project_ids = (CommitLog.where(created_at: today).pluck(:project_id).uniq + Issue.where(created_on: today).pluck(:project_id).uniq).uniq
weekly_project_ids = (CommitLog.where(created_at: current_week).pluck(:project_id).uniq + Issue.where(created_on: current_week).pluck(:project_id).uniq).uniq
month_project_ids = (CommitLog.where(created_at: current_month).pluck(:project_id).uniq + Issue.where(created_on: current_month).pluck(:project_id).uniq).uniq
@day_active_project_count = Project.where(updated_on: today).or(Project.where(id: day_project_ids)).count
@weekly_active_project_count = Project.where(updated_on: current_week).or(Project.where(id: weekly_project_ids)).count
@month_active_project_count = Project.where(updated_on: current_month).or(Project.where(id: month_project_ids)).count
# 新增项目数
@day_new_project_count = Project.where(created_on: today).count
@weekly_new_project_count = Project.where(created_on: current_week).count
@month_new_project_count = Project.where(created_on: current_month).count
end
def month_active_user
@ -16,7 +39,6 @@ class Admins::DashboardsController < Admins::BaseController
{ value: count['professional'].to_i, name: '专业人士' },
{ value: count[nil].to_i, name: '未选职业' },
]
render_ok(data: data)
end
@ -42,10 +64,14 @@ class Admins::DashboardsController < Admins::BaseController
end
def current_week
7.days.ago.beginning_of_day..Time.now.end_of_day
7.days.ago.end_of_day..Time.now.end_of_day
end
def current_month
30.days.ago.beginning_of_day..Time.now.end_of_day
30.days.ago.end_of_day..Time.now.end_of_day
end
def pre_week
14.days.ago.end_of_day..7.days.ago.end_of_day
end
end

View File

@ -0,0 +1,47 @@
class Api::V1::BaseController < ApplicationController
include Api::ProjectHelper
include Api::UserHelper
# before_action :doorkeeper_authorize!
# skip_before_action :user_setup
protected
# def current_user
# #client方法对接需要一直带着用户标识uid
# Rails.logger.info doorkeeper_token
# if doorkeeper_token && doorkeeper_token.resource_owner_id.blank?
# # return User.anonymous if params[:uid].nil?
# # tip_exception("2222")
# # return render_error('缺少用户标识!') if params[:uid].nil?
# User.current = User.find(params[:uid])
# else
# User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
# end
# end
def limit
params.fetch(:limit, 15)
end
def page
params.fetch(:page, 1)
end
# 具有对仓库的管理权限
def require_manager_above
@project = load_project
return render_forbidden unless current_user.admin? && @project.manager?(current_user)
end
# 具有对仓库的操作权限
def require_operate_above
@project = load_project
return render_forbidden unless current_user.admin? && @project.operator?(current_user)
end
# 具有对仓库的访问权限
def require_public_and_member_above
@project = load_project
return render_forbidden unless @project.is_public || (current_user.admin? && @project.member?(current_user))
end
end

View File

@ -0,0 +1,18 @@
class Api::V1::Projects::BranchesController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:all]
def all
@result_object = Api::V1::Projects::Branches::AllListService.call(@project, current_user&.gitea_token)
end
before_action :require_operate_above, only: [:create]
def create
@result_object = Api::V1::Projects::Branches::CreateService.call(@project, branch_params, current_user&.gitea_token)
end
private
def branch_params
params.require(:branch).permit(:new_branch_name, :old_branch_name)
end
end

View File

@ -0,0 +1,12 @@
class Api::V1::Projects::CommitsController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:index, :diff]
def index
@result_object = Api::V1::Projects::Commits::ListService.call(@project, {page: page, limit: limit, sha: params[:sha]}, current_user&.gitea_token)
puts @result_object
end
def diff
@result_object = Api::V1::Projects::Commits::DiffService.call(@project, params[:sha], current_user&.gitea_token)
end
end

View File

@ -0,0 +1,13 @@
class Api::V1::Projects::ContentsController < Api::V1::BaseController
before_action :require_operate_above, only: [:batch]
def batch
@result_object = Api::V1::Projects::Contents::BatchCreateService.call(@project, batch_content_params, current_user&.gitea_token)
puts @result_object
end
private
def batch_content_params
params.require(:content).permit(:author_email, :author_name, :author_timeunix, :branch, :committer_email, :committer_name, :committer_timeunix, :message, :new_branch, files: [ :action_type, :content, :encoding, :file_path])
end
end

View File

@ -0,0 +1,12 @@
class Api::V1::Projects::GitController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:trees, :blobs]
def trees
@result_object = Api::V1::Projects::Git::TreesService.call(@project, params[:sha], {recursive: params[:recursive], page: page, limit: limit}, current_user&.gitea_token)
end
def blobs
@result_object = Api::V1::Projects::Git::BlobsService.call(@project, params[:sha], current_user&.gitea_token)
end
end

View File

@ -0,0 +1,55 @@
class Api::V1::Projects::WebhooksController < Api::V1::BaseController
before_action :require_manager_above
before_action :find_webhook, only: [:show, :update, :destroy, :tests, :hooktasks]
def index
# @result_object = Api::V1::Projects::Webhooks::ListService.call(@project, current_user&.gitea_token)
@webhooks = @project.webhooks
@webhooks = kaminari_paginate(@webhooks)
end
def create
@result_object = Api::V1::Projects::Webhooks::CreateService.call(@project, webhook_params, current_user&.gitea_token)
end
def show
@result_object = Api::V1::Projects::Webhooks::GetService.call(@project, params[:id], current_user&.gitea_token)
end
def update
@result_object = Api::V1::Projects::Webhooks::UpdateService.call(@project, params[:id], webhook_params, current_user&.gitea_token)
end
def destroy
@result_object = Api::V1::Projects::Webhooks::DeleteService.call(@project, params[:id], current_user&.gitea_token)
if @result_object
return render_ok
else
return render_error('删除失败!')
end
end
def tests
@result_object = Api::V1::Projects::Webhooks::TestsService.call(@project, params[:id], current_user&.gitea_token)
if @result_object
return render_ok
else
return render_error('推送失败!')
end
end
def hooktasks
@hooktasks = @webhook.tasks.where(is_delivered: true).order("delivered desc")
@hooktasks = kaminari_paginate(@hooktasks)
end
private
def webhook_params
params.require(:webhook).permit(:active, :branch_filter, :http_method, :url, :content_type, :secret, events: [])
end
def find_webhook
@webhook = Gitea::Webhook.find_by_id(params[:id])
return render_not_found unless @webhook.present?
end
end

View File

@ -0,0 +1,20 @@
class Api::V1::ProjectsController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:show, :compare, :blame]
def index
render_ok
end
def show
@result_object = Api::V1::Projects::GetService.call(@project, current_user.gitea_token)
end
def compare
@result_object = Api::V1::Projects::CompareService.call(@project, params[:from], params[:to], current_user&.gitea_token)
end
def blame
@result_object = Api::V1::Projects::BlameService.call(@project, params[:sha], params[:filepath], current_user&.gitea_token)
puts @result_object
end
end

View File

@ -0,0 +1,13 @@
class Api::V1::Users::ProjectsController < Api::V1::BaseController
before_action :load_observe_user
def index
@object_results = Api::V1::Users::Projects::ListService.call(@observe_user, query_params, current_user)
@projects = kaminari_paginate(@object_results)
end
private
def query_params
params.permit(:category, :is_public, :project_type, :sort_by, :sort_direction, :search)
end
end

View File

@ -0,0 +1,6 @@
class Api::V1::UsersController < Api::V1::BaseController
def index
render_ok
end
end

View File

@ -170,7 +170,6 @@ class ApplicationController < ActionController::Base
# 未授权的捕捉407弹试用申请弹框
def require_login
#6.13 -hs
tip_exception(401, "请登录后再操作") unless User.current.logged?
end
@ -249,39 +248,55 @@ class ApplicationController < ActionController::Base
#return if params[:controller] == "main"
# Find the current user
#Rails.logger.info("current_laboratory is #{current_laboratory} domain is #{request.subdomain}")
User.current = find_current_user
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
# 开放课程通过链接访问的用户
if !User.current.logged? && !params[:chinaoocTimestamp].blank? && !params[:websiteName].blank? && !params[:chinaoocKey].blank?
content = "#{OPENKEY}#{params[:websiteName]}#{params[:chinaoocTimestamp]}"
if Digest::MD5.hexdigest(content) == params[:chinaoocKey]
user = open_class_user
if user
start_user_session(user)
set_autologin_cookie(user)
if request.headers["Authorization"].present? && request.headers["Authorization"].start_with?('Bearer')
tip_exception(401, "请登录后再操作!") unless valid_doorkeeper_token?
if @doorkeeper_token.present?
# client方法对接需要一直带着用户标识uid
if @doorkeeper_token.resource_owner_id.blank?
tip_exception(-1, "缺少用户标识!") if params[:uid].nil?
User.current = User.find(params[:uid])
else
User.current = User.find_by(id: @doorkeeper_token.resource_owner_id)
end
User.current = user
end
end
# if !User.current.logged? && Rails.env.development?
# User.current = User.find 1
# end
else
User.current = find_current_user
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
# 开放课程通过链接访问的用户
if !User.current.logged? && !params[:chinaoocTimestamp].blank? && !params[:websiteName].blank? && !params[:chinaoocKey].blank?
content = "#{OPENKEY}#{params[:websiteName]}#{params[:chinaoocTimestamp]}"
if Digest::MD5.hexdigest(content) == params[:chinaoocKey]
user = open_class_user
if user
start_user_session(user)
set_autologin_cookie(user)
end
User.current = user
end
end
# if !User.current.logged? && Rails.env.development?
# user = User.find 1
# User.current = user
# start_user_session(user)
# end
# 测试版前端需求
logger.info("subdomain:#{request.subdomain}")
if request.subdomain != "www"
if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
User.current = User.find 81403
elsif params[:debug] == 'student'
User.current = User.find 8686
elsif params[:debug] == 'admin'
logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....."
user = User.find 36480
User.current = user
cookies.signed[:user_id] = user.id
# 测试版前端需求
logger.info("subdomain:#{request.subdomain}")
if request.subdomain != "www"
if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
User.current = User.find 81403
elsif params[:debug] == 'student'
User.current = User.find 8686
elsif params[:debug] == 'admin'
logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....."
user = User.find 36480
User.current = user
cookies.signed[:user_id] = user.id
end
end
end
# User.current = User.find 81403
@ -681,7 +696,7 @@ class ApplicationController < ActionController::Base
@project, @owner = Project.find_with_namespace(namespace, id)
if @project and current_user.can_read_project?(@project)
if @project and (current_user.can_read_project?(@project) || controller_path == "projects/project_invite_links")
logger.info "########### has project and can read project"
@project
# elsif @project && current_user.is_a?(AnonymousUser)

View File

@ -33,8 +33,8 @@ class AttachmentsController < ApplicationController
normal_status(-1, "参数缺失") if params[:download_url].blank?
url = URI.encode(params[:download_url].to_s.gsub("http:", "https:"))
if url.starts_with?(base_url)
domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url]
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
url = url.split(base_url)[1].gsub("api", "repos").gsub('?filepath=', '/').gsub('&', '?')
request_url = [domain, api_url, url, "?ref=#{params[:ref]}&access_token=#{current_user&.gitea_token}"].join
response = Faraday.get(request_url)

View File

@ -0,0 +1,27 @@
class CommitLogsController < ApplicationController
def create
tip_exception "未认证" unless params[:token].to_s == "7917908927b6f1b792f2027a08a8b24a2de42c1692c2fd45da0dee5cf90a5af5"
ref = params[:ref]
user_name = params[:pusher][:login]
user_mail = params[:pusher][:email]
user = User.find_by(mail: user_mail)
user = User.find_by(login: user_name) if user.blank?
repository_id = params[:repository][:id]
repository_name = params[:repository][:name]
repository_full_name = params[:repository][:full_name]
owner_name = repository_full_name.split("/")[0]
owner = User.find_by(login: owner_name)
project = Project.where(identifier: repository_name).where(user_id: owner&.id)&.first
project = Project.where(identifier: repository_name).where(gpid: repository_id)&.first if project.blank?
params[:commits].each do |commit|
commit_id = commit[:id]
message = commit[:message]
CommitLog.create(user: user, project: project, repository_id: repository_id,
name: repository_name, full_name: repository_full_name,
ref: ref, commit_id: commit_id, message: message)
end
end
end

View File

@ -6,9 +6,14 @@ class CompareController < ApplicationController
end
def show
load_compare_params
compare
@merge_status, @merge_message = get_merge_message
if params[:type] == "sha"
load_compare_params
@compare_result ||= gitea_compare(@base, @head)
else
load_compare_params
compare
@merge_status, @merge_message = get_merge_message
end
@page_size = page_size <= 0 ? 1 : page_size
@page_limit = page_limit <=0 ? 15 : page_limit
@page_offset = (@page_size -1) * @page_limit

View File

@ -18,15 +18,15 @@ module Acceleratorable
end
def accelerator_domain
Gitea.gitea_config[:accelerator]["domain"]
GiteaService.gitea_config[:accelerator]["domain"]
end
def accelerator_username
Gitea.gitea_config[:accelerator]["access_key_id"]
GiteaService.gitea_config[:accelerator]["access_key_id"]
end
def config_accelerator?
Gitea.gitea_config[:accelerator].present?
GiteaService.gitea_config[:accelerator].present?
end
def is_foreign_url?(clone_addr)

View File

@ -0,0 +1,20 @@
module Api::ProjectHelper
extend ActiveSupport::Concern
def load_project
namespace = params[:owner]
repo = params[:repo]
@project, @owner = Project.find_with_namespace(namespace, repo)
if @project
logger.info "###########project founded"
@project
else
logger.info "###########project not found"
@project = nil
render_not_found and return
end
@project
end
end

View File

@ -0,0 +1,19 @@
module Api::UserHelper
extend ActiveSupport::Concern
def load_observe_user
username = params[:owner]
@observe_user = User.find_by(login: username)
if @observe_user
logger.info "###########observe_user not founded"
@observe_user
else
logger.info "###########observe_user not found"
@observe_user = nil
render_not_found and return
end
@observe_user
end
end

View File

@ -116,6 +116,7 @@ module LoginHelper
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params.merge(hash))
if interactor.success?
Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea success _########"
user.update_column(:is_sync_pwd, true)
true
else
Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea fail!: #{interactor.error}"

View File

@ -5,7 +5,16 @@ module Repository::LanguagesPercentagable
result = Gitea::Repository::Languages::ListService.call(@owner.login,
@repository.identifier, current_user&.gitea_token)
result[:status] === :success ? hash_transform_precentagable(result[:body]) : nil
@transform_language = result[:status] === :success ? hash_transform_precentagable(result[:body]) : nil
update_project_language(@transform_language) unless @transform_language.nil?
@transform_language
end
def update_project_language(language)
db_language = ProjectLanguage.find_or_create_by!(name: language.keys.first.downcase.upcase_first)
@project.update_column(:project_language_id, db_language.id)
rescue
return
end
# hash eq:{"JavaScript": 301681522,"Ruby": 1444004,"Roff": 578781}

View File

@ -0,0 +1,39 @@
class Oauth2Controller < ActionController::Base
layout 'doorkeeper/application'
include LoginHelper
def show
client_id = params[:call_url].split("client_id=")[1].split("&redirect_uri")[0]
@call_url = request.fullpath.split('call_url=').last
@app = Doorkeeper::Application.find_by(uid: client_id)
end
def create
if params[:login].blank?
@error = {msg: '邮箱地址或用户名不能为空', id: 'login'}
elsif params[:password].blank?
@error = {msg: '请输入密码', id: 'password'}
else
@user = User.try_to_login(params[:login], params[:password])
return @error = {msg: '账号或密码错误', id: 'login'} if @user.blank?
return @error = {msg: '违反平台使用规范,账号已被锁定', id: 'login'} if @user.locked?
login_control = LimitForbidControl::UserLogin.new(@user)
return @error = {msg: "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码", id: 'account'} if login_control.forbid?
password_ok = @user.check_password?(params[:password].to_s)
unless password_ok
if login_control.remain_times-1 == 0
@error = {msg: "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码", id: 'account'}
else
@error = {msg: "你已经输错密码#{login_control.error_times+1}次,还剩余#{login_control.remain_times-1}次机会", id: 'account'}
end
login_control.increment!
return
end
login_control.clear
redirect_to params[:call_url] + "&auth=" + @user.login
end
end
end

View File

@ -0,0 +1,42 @@
class Projects::ProjectInviteLinksController < Projects::BaseController
before_action :require_manager!, except: [:show_link, :redirect_link]
before_action :require_login
def current_link
role = params[:role]
is_apply = params[:is_apply]
return render_error('请输入正确的参数!') unless role.present? && is_apply.present?
@project_invite_link = ProjectInviteLink.find_by(user_id: current_user.id, project_id: @project.id, role: role, is_apply: is_apply)
@project_invite_link = ProjectInviteLink.build!(@project, current_user, role, is_apply) unless @project_invite_link.present?
end
def generate_link
ActiveRecord::Base.transaction do
params_data = link_params.merge({user_id: current_user.id, project_id: @project.id})
Projects::ProjectInviteLinks::CreateForm.new(params_data).validate!
@project_invite_link = ProjectInviteLink.build!(project, user, params_data[:role], params_data[:is_apply])
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def show_link
@project_invite_link = ProjectInviteLink.find_by(sign: params[:invite_sign])
return render_not_found unless @project_invite_link.present?
end
def redirect_link
Projects::LinkJoinService.call(current_user, @project, params[:invite_sign])
render_ok
rescue Exception => e
uid_logger_error(e.message)
normal_status(-1, e.message)
end
private
def link_params
params.require(:project_invite_link).permit(:role, :is_apply)
end
end

View File

@ -56,7 +56,12 @@ class PullRequestsController < ApplicationController
end
def create
# return normal_status(-1, "您不是目标分支开发者,没有权限,请联系目标分支作者.") unless @project.operator?(current_user)
if params[:fork_project_id].present?
fork_project= Project.find_by(id: params[:fork_project_id])
return normal_status(-1, "您不是源项目开发者,没有权限,请联系源项目管理员.") unless fork_project && fork_project.operator?(current_user)
else
return normal_status(-1, "您不是项目开发者,没有权限,请联系项目管理员.") unless @project.operator?(current_user)
end
ActiveRecord::Base.transaction do
Issues::CreateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate!
@pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params)
@ -176,6 +181,7 @@ class PullRequestsController < ApplicationController
@issue_assign_to = @issue.get_assign_user
@gitea_pull = Gitea::PullRequest::GetService.call(@owner.login,
@repository.identifier, @pull_request.gitea_number, current_user&.gitea_token)
@last_review = @pull_request.issue.reviews.take
end
def pr_merge

View File

@ -9,7 +9,7 @@ class RepositoriesController < ApplicationController
before_action :load_repository
before_action :authorizate!, except: [:sync_mirror, :tags, :commit, :archive]
before_action :authorizate_user_can_edit_repo!, only: %i[sync_mirror]
before_action :get_ref, only: %i[entries sub_entries top_counts file archive]
before_action :get_ref, only: %i[entries sub_entries top_counts files archive]
before_action :get_latest_commit, only: %i[entries sub_entries top_counts]
before_action :get_statistics, only: %i[top_counts]
@ -54,7 +54,7 @@ class RepositoriesController < ApplicationController
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}/"
@path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
end
end
@ -99,7 +99,7 @@ class RepositoriesController < ApplicationController
end
end
else
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
@path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref)
if interactor.success?
result = interactor.result
@ -222,7 +222,7 @@ class RepositoriesController < ApplicationController
else
result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token)
end
@path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
@path = GiteaService.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
@readme = result[:status] === :success ? result[:body] : nil
@readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
@readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path)
@ -240,8 +240,8 @@ class RepositoriesController < ApplicationController
end
def archive
domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url]
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{Addressable::URI.escape(params[:archive])}"
file_path = [domain, api_url, archive_url].join
@ -253,8 +253,8 @@ class RepositoriesController < ApplicationController
end
def raw
domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url]
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{Addressable::URI.escape(params[:filepath])}?ref=#{Addressable::URI.escape(params[:ref])}"
file_path = [domain, api_url, url].join

View File

@ -0,0 +1,20 @@
class ReviewsController < ApplicationController
before_action :require_login
before_action :load_project
before_action :load_pull_request
def create
return render_forbidden('您不是审查人员,无法进行审查!') if current_user&.id != @pull_request.issue.assigned_to_id
@journal, @review = Api::V1::Projects::PullRequests::Reviews::CreateService.call(@project, @pull_request, review_params, current_user)
end
private
def review_params
params.require(:review).permit(:content, :commit_id, :status)
end
def load_pull_request
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id])
end
end

View File

@ -69,12 +69,18 @@ class Traces::ProjectsController < Traces::BaseController
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]
task_id = params[:task_id]
return render_error("task_id错误") if task_id.blank?
file_save_path = "public/trace_task_results/#{task_id}/report.pdf"
if File.exists?(file_save_path)
redirect_to "/trace_task_results/#{task_id}/report.pdf"
else
render_error("下载报告失败!")
result = Trace::PdfReportService.call(current_user.trace_token, task_id)
if result.is_a?(Hash) && result[:code] == 200
redirect_to result[:download_url]
else
render_error("下载报告失败!")
end
end
rescue Exception => exception
puts exception.message

View File

@ -100,7 +100,7 @@ class UsersController < ApplicationController
def get_image
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
# return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
redirect_to Rails.application.config_for(:configuration)['platform_url'] + "/" + url_to_avatar(@user).to_s
end

View File

@ -1,13 +1,13 @@
---
title: Trustie API Reference
title: GitLink API Reference
language_tabs: # must be one of https://git.io/vQNgJ
- shell: Shell
- javascript: JavaScript
toc_footers:
- <a href='https://www.trustie.net/login?login=false'>Sign Up for a User</a>
- <a href='https://www.trustie.net'>Powered by Trustie</a>
- <a href='https://www.gitlink.org.cn/login'>Sign In for a User</a>
- <a href='https://www.gitlink.org.cn'>Powered by GitLink</a>
includes:
- licenses
@ -31,8 +31,8 @@ code_clipboard: true
# Introduction
Welcome to the Trustie API! You can use our API to access Trustie API endpoints, which can get information on projects, repository, and users in our platform.
Welcome to the GitLink API! You can use our API to access GitLink API endpoints, which can get information on projects, repository, and users in our platform.
We have language bindings in Shell,avaScript! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.
This example API documentation page was created with [Trustie](https://www.trustie.net). Feel free to edit it and use it as a base for your own API's documentation.
This example API documentation page was created with [GitLink](https://www.gitlink.org.cn). Feel free to edit it and use it as a base for your own API's documentation.

View File

@ -1,4 +1,278 @@
# Projects
## 获取项目邀请链接(项目管理员)
当前登录管理员用户获取项目邀请链接的接口第一次请求会默认生成role类型为developer和is_apply为true的链接
> 示例:
```shell
curl -X GET http://localhost:3000/api/yystopf/kellect/project_invite_links/current_link.json
```
```javascript
await octokit.request('GET /api/yystopf/kellect/project_invite_links/current_link.json')
```
### HTTP 请求
`GET /api/:owner/:repo/project_invite_links/current_link.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|role |是| |string |项目权限reporter: 报告者, developer: 开发者manager管理员 |
|is_apply |是| |boolean |是否需要审核 |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |链接id |
|role |string |邀请角色|
|is_apply |boolean |是否需要审核 |
|sign |string |邀请标识(放在链接后面即可)|
|expired_at |string |链接过期时间|
|user.id |int |链接创建者的id |
|user.type |string |链接创建者的类型 |
|user.name |string |链接创建者的名称 |
|user.login |string |链接创建者的标识 |
|user.image_url |string |链接创建者头像 |
|project.id |int |链接关联项目的id |
|project.identifier |string |链接关联项目的标识 |
|project.name |string |链接关联项目的名称 |
|project.description |string |链接关联项目的描述 |
|project.is_public |bool |链接关联项目是否公开 |
|project.owner.id |bool |链接关联项目拥有者id |
|project.owner.type |string |链接关联项目拥有者类型 |
|project.owner.name |string |链接关联项目拥有者昵称 |
|project.owner.login |string |链接关联项目拥有者标识 |
|project.owner.image_url|string |链接关联项目拥有者头像 |
> 返回的JSON示例:
```json
{
"id": 7,
"role": "developer",
"is_apply": false,
"sign": "6b6b454843c291d4e52e60853cb8ad9f",
"expired_at": "2022-06-23 10:08",
"user": {
"id": 2,
"type": "User",
"name": "heh",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
},
"project": {
"id": 474,
"identifier": "kellect",
"name": "kellect",
"description": null,
"is_public": true,
"owner": {
"id": 2,
"type": "User",
"name": "heh",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
}
}
}
```
## 生成项目邀请链接(项目管理员)
当前登录管理员用户生成的项目邀请链接可选role和is_apply参数
> 示例:
```shell
curl -X POST http://localhost:3000/api/yystopf/kellect/project_invite_links/generate_link.json
```
```javascript
await octokit.request('POST /api/yystopf/kellect/project_invite_links/generate_link.json')
```
### HTTP 请求
`POST /api/:owner/:repo/project_invite_links/generate_link.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|role |是| |string |项目权限reporter: 报告者, developer: 开发者manager管理员 |
|is_apply |是| |boolean |是否需要审核 |
> 请求的JSON示例
```json
{
"role": "developer",
"is_apply": false
}
```
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |链接id |
|role |string |邀请角色|
|is_apply |boolean |是否需要审核 |
|sign |string |邀请标识(放在链接后面即可)|
|expired_at |string |链接过期时间|
|user.id |int |链接创建者的id |
|user.type |string |链接创建者的类型 |
|user.name |string |链接创建者的名称 |
|user.login |string |链接创建者的标识 |
|user.image_url |string |链接创建者头像 |
|project.id |int |链接关联项目的id |
|project.identifier |string |链接关联项目的标识 |
|project.name |string |链接关联项目的名称 |
|project.description |string |链接关联项目的描述 |
|project.is_public |bool |链接关联项目是否公开 |
|project.owner.id |bool |链接关联项目拥有者id |
|project.owner.type |string |链接关联项目拥有者类型 |
|project.owner.name |string |链接关联项目拥有者昵称 |
|project.owner.login |string |链接关联项目拥有者标识 |
|project.owner.image_url|string |链接关联项目拥有者头像 |
> 返回的JSON示例:
```json
{
"id": 7,
"role": "developer",
"is_apply": false,
"sign": "6b6b454843c291d4e52e60853cb8ad9f",
"expired_at": "2022-06-23 10:08",
"user": {
"id": 2,
"type": "User",
"name": "heh",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
},
"project": {
"id": 474,
"identifier": "kellect",
"name": "kellect",
"description": null,
"is_public": true,
"owner": {
"id": 2,
"type": "User",
"name": "heh",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
}
}
}
```
## 获取邀请链接信息(被邀请用户)
用户请求邀请链接时,通过该接口来获取链接的信息
> 示例:
```shell
curl -X GET http://localhost:3000/api/yystopf/kellect/project_invite_links/show_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6
```
```javascript
await octokit.request('POST /api/yystopf/kellect/project_invite_links/show_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6')
```
### HTTP 请求
`POST /api/:owner/:repo/project_invite_links/show_link.json?invite_sign=xxx`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|invite_sign |是| |string |项目邀请链接的标识 |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |链接id |
|role |string |邀请角色|
|is_apply |boolean |是否需要审核 |
|sign |string |邀请标识(放在链接后面即可)|
|expired_at |string |链接过期时间|
|user.id |int |链接创建者的id |
|user.type |string |链接创建者的类型 |
|user.name |string |链接创建者的名称 |
|user.login |string |链接创建者的标识 |
|user.image_url |string |链接创建者头像 |
|project.id |int |链接关联项目的id |
|project.identifier |string |链接关联项目的标识 |
|project.name |string |链接关联项目的名称 |
|project.description |string |链接关联项目的描述 |
|project.is_public |bool |链接关联项目是否公开 |
|project.owner.id |bool |链接关联项目拥有者id |
|project.owner.type |string |链接关联项目拥有者类型 |
|project.owner.name |string |链接关联项目拥有者昵称 |
|project.owner.login |string |链接关联项目拥有者标识 |
|project.owner.image_url|string |链接关联项目拥有者头像 |
> 返回的JSON示例:
```json
{
"id": 7,
"role": "developer",
"is_apply": false,
"sign": "6b6b454843c291d4e52e60853cb8ad9f",
"expired_at": "2022-06-23 10:08",
"user": {
"id": 2,
"type": "User",
"name": "heh",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
},
"project": {
"id": 474,
"identifier": "kellect",
"name": "kellect",
"description": null,
"is_public": true,
"owner": {
"id": 2,
"type": "User",
"name": "heh",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
}
}
}
```
## 接受项目邀请链接(被邀请用户)
当前登录(非项目)用户加入项目的接口,如果项目链接不需要审核,请求成功后即加入项目,如果需要审核,那么会提交一个申请,需要项目管理员审核
> 示例:
```shell
curl -X POST http://localhost:3000/api/yystopf/kellect/project_invite_links/redirect_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6
```
```javascript
await octokit.request('POST /api/yystopf/kellect/project_invite_links/redirect_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6')
```
### HTTP 请求
`POST /api/:owner/:repo/project_invite_links/redirect_link.json?invite_sign=xxx`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|invite_sign |是| |string |项目邀请链接的标识 |
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 申请加入项目
申请加入项目

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,105 @@ await octokit.request('GET /api/users/me.json')
Success Data.
</aside>
## 用户项目列表
获取用户项目列表
> 示例:
```shell
curl -X GET http://localhost:3000/api/v1/:login/projects.json
```
```javascript
await octokit.request('GET /api/v1/:login/projects.json')
```
### HTTP 请求
`GET api/v1/yystopf/projects.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login | string | 用户的用户名|
|category | string | 归属项目,默认为全部项目, join: 参与项目, created: 创建项目, manage: 管理项目, watched: 关注项目, forked: 复刻项目 |
|is_public | boolean | 公/私有项目true为公开项目false为私有项目|
|project_type | string | 项目类型common为托管项目, mirror为镜像项目, sync_mirror为同步镜像项目|
|sort_by | string | 项目的排序字段,比如 created_on, updated_on等|
|sort_direction | string | 排序的类型desc 倒序asc 正序|
|limit | integer | 每页个数 |
|page | integer | 页码 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|total_count | integer | 项目总数 |
|projects.owner.id | integer | 项目拥有者id |
|projects.owner.type | string | 项目拥有者类型User 用户Organization 组织 |
|projects.owner.name | string | 项目拥有者名称|
|projects.owner.login | string | 项目拥有者用户名 |
|projects.owner.image_url | string | 项目拥有者头像地址 |
|type | string | 项目类型common 托管项目mirror 镜像项目, sync 同步镜像项目 |
|description | string | 项目描述 |
|forked_count | integer | 项目复刻数量 |
|forked_from_project_id | integer | 项目复刻来源项目 |
|identifier | string | 项目标识 |
|issues_count | integer | 项目issues数量|
|pull_requests_count | integer | 项目合并请求数量 |
|invite_code | string | 项目邀请码|
|website | string | 项目网址|
|platform | string | 项目平台 |
|name | string | 项目名称|
|open_devops | boolean | 项目是否开启工作流 |
|praises_count | integer | 项目点赞数量|
|is_public | boolean | 项目是否公开|
|status | integer | 项目状态|
|watchers_count | integer | 项目关注数量|
|ignore_id | integer | 项目ignoreID|
|license_id | integer | 项目许可证ID|
|project_category_id | integer | 项目分类ID|
|project_language_id | integer | 项目语言ID|
> 返回的JSON示例:
```json
"total_count": 1,
"projects": [
{
"owner": {
"id": 2,
"type": "User",
"name": "heh",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
},
"type": "common",
"description": null,
"forked_count": 1,
"forked_from_project_id": null,
"identifier": "hahahah",
"issues_count": 5,
"pull_requests_count": 3,
"invite_code": "8zfKtM",
"website": null,
"platform": "forge",
"name": "hahahah",
"open_devops": false,
"praises_count": 0,
"is_public": true,
"status": 1,
"watchers_count": 0,
"ignore_id": null,
"license_id": null,
"project_category_id": null,
"project_language_id": null
}
]
```
<aside class="success">
Success Data.
</aside>
## 用户消息列表
获取用户消息列表

View File

@ -0,0 +1,8 @@
class Projects::ProjectInviteLinks::CreateForm < BaseForm
attr_accessor :user_id, :project_id, :role, :is_apply
validates :user_id, :project_id, :role, presence: true
validates :role, inclusion: { in: %w(manager developer reporter), message: "请输入正确的权限." }
validates :is_apply, inclusion: {in: [true, false], message: "请输入是否需要管理员审核."}
end

View File

@ -30,7 +30,7 @@ module ProjectsHelper
end
def gitea_domain
Gitea.gitea_config[:domain]
GiteaService.gitea_config[:domain]
end
def find_user_by_login_or_mail(identifier)

View File

@ -1,218 +1,217 @@
module RepositoriesHelper
def render_permission(user, project)
return "Admin" if user&.admin?
project.get_premission(user)
end
def render_decode64_content(str)
return nil if str.blank?
Base64.decode64(str).force_encoding("UTF-8").encode("UTF-8", invalid: :replace)
end
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.include?(str&.downcase) || str.blank?
end
def image_type?(str)
default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd)
default_type.include?(str&.downcase)
end
def is_readme?(type, str)
return false if type != 'file' || str.blank?
readme_types = ["readme.md", "readme", "readme_en.md", "readme_zh.md", "readme_en", "readme_zh"]
readme_types.include?(str.to_s.downcase) || str =~ CustomRegexp::MD_REGEX
end
def render_commit_author(author_json)
return nil if author_json.blank? || (author_json["id"].blank? && author_json['name'].blank?)
if author_json["id"].present?
return find_user_by_gitea_uid author_json['id']
end
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"])
end
end
def render_cache_commit_author(author_json)
if author_json["name"].present? && author_json["email"].present?
return find_user_in_redis_cache(author_json['name'], author_json['email'])
end
if author_json["Name"].present? && author_json["Email"].present?
return find_user_in_redis_cache(author_json['Name'], author_json['Email'])
end
end
def readme_render_decode64_content(str, owner, repo, ref, path)
return nil if str.blank?
begin
content = Base64.decode64(str).force_encoding('UTF-8')
c_regex = /\!\[.*?\]\((.*?)\)/
src_regex = /src=\"(.*?)\"/
src2_regex = /src='(.*?)'/
ss = content.to_s.scan(c_regex)
ss_src = content.scan(src_regex)
ss_src2 = content.scan(src2_regex)
total_images = ss + ss_src + ss_src2
if total_images.length > 0
total_images.each do |s|
begin
image_title = /\"(.*?)\"/
r_content = s[0]
remove_title = r_content.to_s.scan(image_title)
# if remove_title.length > 0
# r_content = r_content.gsub(/#{remove_title[0]}/, "").strip
# end
path_last = r_content
path_current = ""
# 相对路径处理
if r_content.start_with?("../")
relative_path_length = r_content.split("../").size - 1
path_pre = path.split("/").size - 1 - relative_path_length
path_pre = 0 if path_pre < 0
path_current = path_pre == 0 ? "" : path.split("/")[0..path_pre].join("/")
path_last = r_content.split("../").last
elsif r_content.start_with?("/") # 根路径处理
path_last = r_content[1..r_content.size]
else
path_current = path
end
# if r_content.include?("?")
# new_r_content = r_content + "&raw=true"
# else
# new_r_content = r_content + "?raw=true"
# end
new_r_content = r_content
unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:")
# 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
end
content = content.gsub(/src=\"#{r_content}\"/, "src=\"#{new_r_content}\"").gsub(/src='#{r_content}'/, "src=\"#{new_r_content}\"")
rescue
next
end
end
end
return content
rescue
return str
end
end
# author hui.he
def new_readme_render_decode64_content(str, owner, repo, ref, readme_path, readme_name)
file_path = readme_path.include?('/') ? readme_path.gsub("/#{readme_name}", '') : readme_path.gsub("#{readme_name}", '')
return nil if str.blank?
content = Base64.decode64(str).force_encoding('UTF-8')
s_regex = /\[.*?\]\((.*?)\)/
src_regex = /src=\"(.*?)\"/
ss = content.to_s.scan(s_regex)
ss_src = content.to_s.scan(src_regex)
total_sources = ss + ss_src
total_sources.uniq!
total_sources.each do |s|
begin
s_content = s[0]
# 链接直接跳过不做替换
next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') || s_content.blank?
ext = File.extname(s_content)[1..-1]
if image_type?(ext) || download_type(ext)
s_content = File.expand_path(s_content, file_path)
s_content = s_content.split("#{Rails.root}/")[1]
# content = content.gsub(s[0], "/#{s_content}")
s_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{s_content}&ref=#{ref}"].join
content = content.gsub(s[0], s_content)
else
path = [owner&.login, repo&.identifier, 'tree', ref, file_path].join("/")
s_content = File.expand_path(s_content, path)
s_content = s_content.split("#{Rails.root}/")[1]
content = content.gsub(s[0], "/#{s_content}")
end
rescue
next
end
end
return content
rescue
return str
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
def readme_decode64_content(entry, owner, repo, ref, path=nil)
Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}")
content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
Rails.logger.info("content===#{content}")
# readme_render_decode64_content(content, owner, repo, ref)
new_readme_render_decode64_content(content, owner, repo, ref, entry['path'], entry['name'])
end
def decode64_content(entry, owner, repo, ref, path=nil)
if is_readme?(entry['type'], entry['name'])
Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}")
content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
Rails.logger.info("content===#{content}")
# 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
module RepositoriesHelper
def render_permission(user, project)
return "Admin" if user&.admin?
project.get_premission(user)
end
def render_decode64_content(str)
return nil if str.blank?
Base64.decode64(str).force_encoding("UTF-8").encode("UTF-8", invalid: :replace)
end
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 apk)
default_type.include?(str&.downcase) || str.blank?
end
def image_type?(str)
default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd)
default_type.include?(str&.downcase)
end
def is_readme?(type, str)
return false if type != 'file' || str.blank?
readme_types = ["readme.md", "readme", "readme_en.md", "readme_zh.md", "readme_en", "readme_zh"]
readme_types.include?(str.to_s.downcase) || str =~ CustomRegexp::MD_REGEX
end
def render_commit_author(author_json)
return nil if author_json.blank? || (author_json["id"].blank? && author_json['name'].blank?)
if author_json["id"].present?
return find_user_by_gitea_uid author_json['id']
end
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"])
end
end
def render_cache_commit_author(author_json)
if author_json["name"].present? && author_json["email"].present?
return find_user_in_redis_cache(author_json['name'], author_json['email'])
end
if author_json["Name"].present? && author_json["Email"].present?
return find_user_in_redis_cache(author_json['Name'], author_json['Email'])
end
end
def readme_render_decode64_content(str, owner, repo, ref, path)
return nil if str.blank?
begin
content = Base64.decode64(str).force_encoding('UTF-8')
c_regex = /\!\[.*?\]\((.*?)\)/
src_regex = /src=\"(.*?)\"/
src2_regex = /src='(.*?)'/
ss = content.to_s.scan(c_regex)
ss_src = content.scan(src_regex)
ss_src2 = content.scan(src2_regex)
total_images = ss + ss_src + ss_src2
if total_images.length > 0
total_images.each do |s|
begin
image_title = /\"(.*?)\"/
r_content = s[0]
remove_title = r_content.to_s.scan(image_title)
# if remove_title.length > 0
# r_content = r_content.gsub(/#{remove_title[0]}/, "").strip
# end
path_last = r_content
path_current = ""
# 相对路径处理
if r_content.start_with?("../")
relative_path_length = r_content.split("../").size - 1
path_pre = path.split("/").size - 1 - relative_path_length
path_pre = 0 if path_pre < 0
path_current = path_pre == 0 ? "" : path.split("/")[0..path_pre].join("/")
path_last = r_content.split("../").last
elsif r_content.start_with?("/") # 根路径处理
path_last = r_content[1..r_content.size]
else
path_current = path
end
# if r_content.include?("?")
# new_r_content = r_content + "&raw=true"
# else
# new_r_content = r_content + "?raw=true"
# end
new_r_content = r_content
unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:")
# 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
end
content = content.gsub(/src=\"#{r_content}\"/, "src=\"#{new_r_content}\"").gsub(/src='#{r_content}'/, "src=\"#{new_r_content}\"")
rescue
next
end
end
end
return content
rescue
return str
end
end
# author hui.he
def new_readme_render_decode64_content(str, owner, repo, ref, readme_path, readme_name)
file_path = readme_path.include?('/') ? readme_path.gsub("/#{readme_name}", '') : readme_path.gsub("#{readme_name}", '')
return nil if str.blank?
content = Base64.decode64(str).force_encoding('UTF-8')
s_regex = /\[.*?\]\((.*?)\)/
src_regex = /src=\"(.*?)\"/
ss = content.to_s.scan(s_regex)
ss_src = content.to_s.scan(src_regex)
total_sources = ss + ss_src
total_sources.uniq!
total_sources.each do |s|
begin
s_content = s[0]
# 链接直接跳过不做替换
next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') || s_content.blank?
ext = File.extname(s_content)[1..-1]
if (image_type?(ext) || download_type(ext)) && !ext.blank?
s_content = File.expand_path(s_content, file_path)
s_content = s_content.split("#{Rails.root}/")[1]
# content = content.gsub(s[0], "/#{s_content}")
s_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{s_content}&ref=#{ref}"].join
content = content.gsub(s[0], s_content)
else
path = [owner&.login, repo&.identifier, 'tree', ref, file_path].join("/")
s_content = File.expand_path(s_content, path)
s_content = s_content.split("#{Rails.root}/")[1]
content = content.gsub('('+s[0]+')', '('+"/#{s_content}"+')')
end
rescue
next
end
end
return content
rescue
return str
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
def readme_decode64_content(entry, owner, repo, ref, path=nil)
Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}")
content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
# Rails.logger.info("content===#{content}")
# readme_render_decode64_content(content, owner, repo, ref)
new_readme_render_decode64_content(content, owner, repo, ref, entry['path'], entry['name'])
end
def decode64_content(entry, owner, repo, ref, path=nil)
if is_readme?(entry['type'], entry['name'])
Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}")
content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
# Rails.logger.info("content===#{content}")
# 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

@ -44,8 +44,8 @@ module Gitea
def token
{
username: Gitea.gitea_config[:access_key_id],
password: Gitea.gitea_config[:access_key_secret]
username: GiteaService.gitea_config[:access_key_id],
password: GiteaService.gitea_config[:access_key_secret]
}
end
end

View File

@ -37,7 +37,7 @@ class Ci::Drone::Server
private
def gitea_url
Gitea.gitea_config[:domain]
GiteaService.gitea_config[:domain]
end
def database_username

View File

@ -1,4 +1,4 @@
module Gitea
module GiteaService
class << self
def gitea_config
gitea_config = {}

View File

@ -2,24 +2,27 @@
#
# Table name: forge_applied_projects
#
# id :integer not null, primary key
# project_id :integer
# user_id :integer
# role :integer default("0")
# status :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# id :integer not null, primary key
# project_id :integer
# user_id :integer
# role :integer default("0")
# status :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# project_invite_link_id :integer
#
# Indexes
#
# index_forge_applied_projects_on_project_id (project_id)
# index_forge_applied_projects_on_user_id (user_id)
# index_forge_applied_projects_on_project_id (project_id)
# index_forge_applied_projects_on_project_invite_link_id (project_invite_link_id)
# index_forge_applied_projects_on_user_id (user_id)
#
class AppliedProject < ApplicationRecord
self.table_name = "forge_applied_projects"
belongs_to :user
belongs_to :project
belongs_to :project_invite_link, optional: true
has_many :applied_messages, as: :applied, dependent: :destroy
# has_many :forge_activities, as: :forge_act, dependent: :destroy

6
app/models/commit_log.rb Normal file
View File

@ -0,0 +1,6 @@
class CommitLog < ApplicationRecord
belongs_to :user
belongs_to :project
belongs_to :repository
end

View File

@ -69,6 +69,7 @@ class Issue < ApplicationRecord
has_many :issue_tags, through: :issue_tags_relates
has_many :issue_times, dependent: :destroy
has_many :issue_depends, dependent: :destroy
has_many :reviews, dependent: :destroy
scope :issue_includes, ->{includes(:user)}
scope :issue_many_includes, ->{includes(journals: :user)}
scope :issue_issue, ->{where(issue_classify: [nil,"issue"])}

View File

@ -12,11 +12,13 @@
# parent_id :integer
# comments_count :integer default("0")
# reply_id :integer
# review_id :integer
#
# Indexes
#
# index_journals_on_created_on (created_on)
# index_journals_on_journalized_id (journalized_id)
# index_journals_on_review_id (review_id)
# index_journals_on_user_id (user_id)
# journals_journalized_id (journalized_id,journalized_type)
#

View File

@ -61,6 +61,7 @@ class MessageTemplate::CustomTip < MessageTemplate
end
title.gsub!('{platform}', PLATFORM)
content.gsub!('{platform}', PLATFORM)
content.gsub!('{baseurl}', base_url)
return title, content
rescue => e

View File

@ -125,6 +125,7 @@ class Project < ApplicationRecord
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 :user_trace_tasks, dependent: :destroy
has_many :project_invite_links, dependent: :destroy
after_create :incre_user_statistic, :incre_platform_statistic
after_save :check_project_members
before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data
@ -138,7 +139,7 @@ class Project < ApplicationRecord
delegate :content, to: :project_detail, allow_nil: true
delegate :name, to: :license, prefix: true, allow_nil: true
validate :validate_sensitive_string
validate :validate_sensitive_string, on: [:create, :update]
def self.all_visible(user_id=nil)
user_projects_sql = Project.joins(:owner).where(users: {type: 'User'}).to_sql
@ -184,7 +185,7 @@ class Project < ApplicationRecord
forked_project = self.forked_from_project
if forked_project.present?
forked_project.decrement(:forked_count, 1)
forked_project.save
forked_project.update_column(:forked_count, forked_project.forked_count)
end
end
@ -373,7 +374,7 @@ class Project < ApplicationRecord
logger.info "########namespace_path: #{namespace_path} ########identifier: #{identifier} "
user = Owner.find_by_login namespace_path
user = Owner.new(login: namespace_path) if user.nil?
user = User.new(login: namespace_path) if user.nil?
project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}")
return nil if project.blank?

View File

@ -0,0 +1,59 @@
# == Schema Information
#
# Table name: project_invite_links
#
# id :integer not null, primary key
# project_id :integer
# user_id :integer
# role :integer default("4")
# is_apply :boolean default("1")
# sign :string(255)
# expired_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_invite_links_on_project_id (project_id)
# index_project_invite_links_on_sign (sign)
# index_project_invite_links_on_user_id (user_id)
#
class ProjectInviteLink < ApplicationRecord
default_scope { where("expired_at > ?", Time.now).or(where(expired_at: nil)) }
belongs_to :project
belongs_to :user
has_many :applied_projects
scope :with_project_id, -> (project_id) {where(project_id: project_id)}
scope :with_user_id, -> (user_id) {where(user_id: user_id)}
enum role: {manager: 3, developer: 4, reporter: 5}
before_create :set_old_data_expired_at
def self.random_hex_sign
hex = (SecureRandom.hex(32))
return hex unless ProjectInviteLink.where(sign: hex).exists?
end
def self.build!(project, user, role="developer", is_apply=true)
self.create!(
project_id: project&.id,
user_id: user&.id,
role: role,
is_apply: is_apply,
sign: random_hex_sign,
expired_at: Time.now + 3.days
)
end
private
def set_old_data_expired_at
ProjectInviteLink.where(user_id: self.user_id, project_id: self.project, role: self.role, is_apply: self.is_apply).update_all(expired_at: Time.now)
end
end

View File

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

27
app/models/review.rb Normal file
View File

@ -0,0 +1,27 @@
# == Schema Information
#
# Table name: reviews
#
# id :integer not null, primary key
# issue_id :integer
# reviewer_id :integer
# content :text(65535)
# commit_id :string(255)
# status :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_reviews_on_issue_id (issue_id)
# index_reviews_on_reviewer_id (reviewer_id)
#
class Review < ApplicationRecord
belongs_to :issue
belongs_to :reviewer, class_name: 'User', foreign_key: :reviewer_id
has_one :journal, dependent: :destroy
enum status: {common: 0, approved: 1, rejected: 2}
end

View File

@ -181,7 +181,7 @@ class User < Owner
scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) }
scope :like, lambda { |keywords|
sql = "CONCAT(lastname, firstname) LIKE :search OR nickname LIKE :search OR login LIKE :search OR mail LIKE :search OR nickname LIKE :search"
where(sql, :search => "%#{keywords.split(" ").join('|')}%") unless keywords.blank?
where(sql, :search => "%#{keywords.strip}%") unless keywords.blank?
}
scope :simple_select, -> {select(:id, :login, :lastname,:firstname, :nickname, :gitea_uid, :type)}
@ -567,7 +567,8 @@ class User < Owner
return '游客' unless logged?
name = lastname + firstname
name = name.blank? ? (nickname.blank? ? login : nickname) : name
name.gsub(/\s+/, '').strip #6.11 -hs
# name.gsub(/\s+/, '').strip #6.11 -hs
name.strip
end
def only_real_name
@ -684,6 +685,21 @@ class User < Owner
raise text
end
def self.authenticate!(login, password)
user = self.where("login = ? or mail = ? or phone = ? ", login.to_s.gsub(" ",''),login.to_s.gsub(" ",''),login.downcase.to_s.gsub(" ",'')).limit(1).first
return (user.check_password?(password) ? user : nil) unless user.nil?
nil
end
# Generate public/private keys
def generate_keys
key_size = (Rails.env == 'test' ? 512 : 2048)
serialized_private_key = OpenSSL::PKey::RSA::generate(key_size).to_s
serialized_public_key = OpenSSL::PKey::RSA.new(serialized_private_key)
[serialized_private_key, serialized_public_key]
end
def show_real_name
name = lastname + firstname
if name.blank?

View File

@ -0,0 +1,38 @@
class Api::V1::Projects::BlameService < ApplicationService
include ActiveModel::Model
attr_reader :project, :sha, :filepath, :owner, :repo, :token
attr_accessor :gitea_data
validates :sha, :filepath, presence: true
def initialize(project, sha, filepath, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@sha = sha
@filepath = filepath
@token = token
end
def call
raise Error, errors.full_messages.join(",") unless valid?
load_gitea_data
gitea_data
end
private
def request_params
{
access_token: token,
sha: sha,
filepath: filepath
}
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_blame_by_owner_repo(owner, repo, {query: request_params})
raise Error, '获取项目blame失败' unless @gitea_data.is_a?(Hash)
end
end

View File

@ -0,0 +1,30 @@
class Api::V1::Projects::Branches::AllListService < ApplicationService
attr_accessor :project, :token, :owner, :repo
attr_accessor :gitea_data
def initialize(project, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@token = token
end
def call
load_gitea_data
gitea_data
end
private
def request_params
{
access_token: token
}
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_branch_name_set_by_owner_repo(owner, repo, {query: request_params}) rescue nil
raise Error, '获取所有分支失败!' unless @gitea_data.is_a?(Hash)
end
end

View File

@ -0,0 +1,51 @@
class Api::V1::Projects::Branches::CreateService < ApplicationService
include ActiveModel::Model
attr_accessor :project, :token, :owner, :repo, :old_branch_name, :new_branch_name
attr_accessor :gitea_data
validates :new_branch_name, :old_branch_name, presence: true
def initialize(project, params, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@new_branch_name = params[:new_branch_name]
@old_branch_name = params[:old_branch_name]
@token = token
end
def call
raise Error, errors.full_messages.join(",") unless valid?
check_new_branch_exist
excute_data_to_gitea
gitea_data
end
private
def request_params
{
access_token: token
}
end
def request_body
{
new_branch_name: new_branch_name,
old_branch_name: old_branch_name,
}
end
def excute_data_to_gitea
@gitea_data = $gitea_client.post_repos_branches_by_owner_repo(owner, repo, {body: request_body.to_json, query: request_params}) rescue nil
raise Error, '创建分支失败!' unless @gitea_data.is_a?(Hash)
end
def check_new_branch_exist
result = $gitea_client.get_repos_branch_name_set_by_owner_repo(owner, repo, {query: request_params}) rescue nil
raise Error, '查询分支名称失败!' unless result.is_a?(Hash)
raise Error, '分支已存在!' if result['branch_name'].include?(@new_branch_name)
end
end

View File

@ -0,0 +1,36 @@
class Api::V1::Projects::Commits::DiffService < ApplicationService
include ActiveModel::Model
attr_accessor :project, :sha, :token, :owner, :repo
attr_accessor :gitea_data
validates :sha, presence: true
def initialize(project, sha, token = nil)
@project = project
@sha = sha
@owner = project&.owner.login
@repo = project&.identifier
@token = token
end
def call
raise Error, errors.full_messages.join(",") unless valid?
load_gitea_data
gitea_data
end
private
def request_params
{
access_token: token
}
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_commits_diff_by_owner_repo_sha(owner, repo, sha, {query: request_params}) rescue nil
raise Error, '获取提交对比失败!' unless @gitea_data.is_a?(Hash)
end
end

View File

@ -0,0 +1,38 @@
class Api::V1::Projects::Commits::ListService < ApplicationService
attr_reader :project, :sha, :page, :limit, :owner, :repo, :token
attr_accessor :gitea_data
def initialize(project, params, token=nil)
@project = project
@sha = params[:sha]
@page = params[:page] || 1
@limit = params[:limit] || 15
@owner = project&.owner.login
@repo = project&.identifier
@token = token
end
def call
load_gitea_data
gitea_data
end
private
def request_params
param = {
access_token: token,
page: page,
limit: limit
}
param.merge!(sha: sha) if sha.present?
param
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_commits_by_owner_repo(owner, repo, {query: request_params}) rescue nil
raise Error, '获取提交列表失败!' unless @gitea_data.is_a?(Hash)
end
end

View File

@ -0,0 +1,34 @@
class Api::V1::Projects::CompareService < ApplicationService
include ActiveModel::Model
attr_reader :project, :from, :to, :token, :owner, :repo
attr_accessor :gitea_data
validates :from, :to, presence: true
def initialize(project, from, to, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@from = from
@to = to
@token = token
end
def call
raise Error, errors.full_messages.join(",") unless valid?
load_gitea_data
raise Error, '获取对比信息失败!' unless gitea_data.is_a?(Hash)
gitea_data
end
private
def request_params
{
access_token: token
}
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_compare_by_owner_repo_from_to(owner, repo, from, to, {query: request_params}) rescue nil
end
end

View File

@ -0,0 +1,91 @@
class Api::V1::Projects::Contents::BatchCreateService < ApplicationService
include ActiveModel::Model
attr_reader :project, :owner, :repo, :token
attr_reader :files, :author_email, :author_name, :author_timeunix, :branch, :committer_email, :committer_name, :committer_timeunix, :message, :new_branch
attr_accessor :gitea_data
validates :author_email, :committer_email, presence: true, format: { with: CustomRegexp::EMAIL }
validates :author_name, :committer_name, presence: true, format: { with: /\A(?!_)(?!.*?_$)[a-zA-Z0-9_-]{4,15}\z/ }
validates :author_timeunix, :committer_timeunix, presence: true
validates :branch, presence: true
validates :message, presence: true
def initialize(project, params, token=nil)
puts params
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@token = token
@files = params[:files]
@author_email = params[:author_email]
@author_name = params[:author_name]
@author_timeunix = params[:author_timeunix]
@branch = params[:branch]
@committer_email = params[:committer_email]
@committer_name = params[:committer_name]
@committer_timeunix = params[:committer_timeunix]
@message = params[:message]
@new_branch = params[:new_branch]
end
def call
raise Error, '请输入正确的文件参数Files' unless valid_files?
raise Error, errors.full_messages.join(", ") unless valid?
check_branch_exist
excute_data_to_gitea
gitea_data
end
private
def request_params
{
access_token: token
}
end
def request_body
{
files: files,
header: {
author: {
email: author_email,
name: author_name
},
committer: {
email: committer_email,
name: committer_name
},
dates: {
author: Time.at(author_timeunix.to_i),
committer: Time.at(committer_timeunix.to_i)
},
message: message,
new_branch: new_branch,
signoff: false
}
}
end
def valid_files?
return false unless files.is_a?(Array) && !files.blank?
files.each do |file|
return false unless file.has_key?(:action_type) && file.has_key?(:content) && file.has_key?(:encoding) && file.has_key?(:file_path)
end
end
def excute_data_to_gitea
@gitea_data = $gitea_client.post_repos_contents_batch_by_owner_repo(owner, repo, {body: request_body.to_json, query: request_params}) rescue nil
raise Error, '创建文件失败!' unless @gitea_data.is_a?(Hash)
end
def check_branch_exist
result = $gitea_client.get_repos_branch_name_set_by_owner_repo(owner, repo, {query: request_params} ) rescue nil
raise Error, '查询分支名称失败!' unless result.is_a?(Hash)
raise Error, '分支不存在!' unless result['branch_name'].include?(branch)
raise Error, '分支已存在!' if result['branch_name'].include?(new_branch) && new_branch.nil?
end
end

View File

@ -0,0 +1,50 @@
class Api::V1::Projects::GetService < ApplicationService
attr_reader :project, :token, :owner, :repo
attr_accessor :gitea_data, :gitea_branch_tag_count
def initialize(project, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@token = token
end
def call
load_gitea_data
load_gitea_branch_tag_count
result_object
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
private
def request_params
{
access_token: token
}
end
def result_object
{
full_name: "#{owner}/#{repo}",
owner: project&.owner,
ssh_url: gitea_data["ssh_url"],
clone_url: gitea_data["clone_url"],
size: gitea_data["size"],
default_branch: gitea_data["default_branch"],
empty: gitea_data["empty"],
branch_count: gitea_branch_tag_count["branch_count"],
tag_count: gitea_branch_tag_count["tag_count"],
}
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_by_owner_repo(owner, repo, {query: request_params}) rescue nil
end
def load_gitea_branch_tag_count
@gitea_branch_tag_count = $gitea_client.get_repos_branch_tag_count_by_owner_repo(owner, repo, {query: request_params}) rescue nil
end
end

View File

@ -0,0 +1,34 @@
class Api::V1::Projects::Git::BlobsService < ApplicationService
include ActiveModel::Model
attr_accessor :project, :sha, :token, :owner, :repo
attr_accessor :gitea_data
validates :sha, presence: :true
def initialize(project, sha, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@sha = sha
@token = token
end
def call
load_gitea_data
gitea_data
end
private
def request_params
{
access_token: token
}
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_git_blobs_by_owner_repo_sha(owner, repo, sha, {query: request_params}) rescue nil
end
end

View File

@ -0,0 +1,52 @@
class Api::V1::Projects::Git::TreesService < ApplicationService
include ActiveModel::Model
attr_accessor :project, :token, :sha, :recursive, :page, :limit, :owner, :repo
attr_accessor :gitea_data
validates :sha, presence: :true
validates :recursive, inclusion: {in: [nil, '', true, false]}
def initialize(project, sha, params, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@token = token
@sha = sha
@recursive = params[:recursive]
@page = params[:page] || 1
@limit = params[:limit] || 15
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
$gitea_client.token = token unless token.blank?
load_gitea_data
$gitea_client.token = nil unless token.blank?
gitea_data
end
private
def request_query
if recursive.present?
{
recursive: recursive,
page: page,
per_page: limit,
access_token: token
}
else
{
page: page,
per_page: limit,
access_token: token
}
end
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_git_trees_by_owner_repo_sha(owner, repo, sha, {query: request_query}) rescue nil
raise Error, '获取文件树列表失败!' unless @gitea_data.is_a?(Hash)
end
end

View File

@ -0,0 +1,40 @@
class Api::V1::Projects::PullRequests::Reviews::CreateService < ApplicationService
include ActiveModel::Model
attr_reader :project, :pull_request, :issue, :status, :commit_id, :content, :current_user
attr_accessor :review, :journal
validates :status, inclusion: { in: %w(common approved rejected), message: '请输入正确的Type'}
def initialize(project, pull_request, params, current_user)
@project = project
@pull_request = pull_request
@issue = pull_request&.issue
@status = params[:status]
@commit_id = params[:commit_id]
@content = params[:content]
@current_user = current_user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
ActiveRecord::Base.transaction do
create_review
create_journal
end
return @journal, @review
rescue
raise Error, '服务器错误,请联系系统管理员!'
end
private
def create_review
@review = issue.reviews.create!(status: status, content: content, commit_id: commit_id, reviewer_id: @current_user.id)
end
def create_journal
@journal = issue.journals.create!(notes: content, user_id: @current_user.id, review_id: @review.id)
end
end

View File

@ -0,0 +1,62 @@
class Api::V1::Projects::Webhooks::CreateService < ApplicationService
include ActiveModel::Model
attr_reader :project, :token, :owner, :repo, :active, :branch_filter, :content_type, :url, :http_method, :secret, :events
attr_accessor :gitea_data
validates :url, format: { with: URI::regexp(%w[http https]), message: "请输入正确的地址" }
validates :active, inclusion: {in: [true, false]}
validates :http_method, inclusion: { in: %w(POST GET), message: "请输入正确的请求方式"}
validates :content_type, inclusion: { in: %w(json form), message: "请输入正确的Content Type"}
def initialize(project, params, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@active = params[:active]
@branch_filter = params[:branch_filter]
@content_type = params[:content_type]
@url = params[:url]
@http_method = params[:http_method]
@secret = params[:secret]
@events = params[:events]
@token = token
end
def call
raise Error, errors.full_messages.join(",") unless valid?
begin
excute_data_to_gitea
gitea_data
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
end
private
def request_params
{
access_token: token
}
end
def request_body
{
active: active,
branch_filter: branch_filter,
config: {
content_type: content_type,
url: url,
http_method: http_method,
secret: secret
},
events: events || [],
type: 'gitea',
}
end
def excute_data_to_gitea
@gitea_data = $gitea_client.post_repos_hooks_by_owner_repo(owner, repo, {body: request_body.to_json, query: request_params}) rescue nil
end
end

View File

@ -0,0 +1,32 @@
class Api::V1::Projects::Webhooks::DeleteService < ApplicationService
attr_reader :project, :id, :token, :owner, :repo
attr_accessor :gitea_data
def initialize(project, id, token=nil)
@project = project
@id = id
@owner = project&.owner.login
@repo = project&.identifier
@token = token
end
def call
excute_data_to_gitea
gitea_data
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
private
def request_params
{
access_token: token
}
end
def excute_data_to_gitea
@gitea_data = $gitea_client.delete_repos_hooks_by_owner_repo_id(owner, repo, id, {query: request_params}) rescue nil
end
end

View File

@ -0,0 +1,33 @@
class Api::V1::Projects::Webhooks::GetService < ApplicationService
attr_reader :project, :id, :token, :owner, :repo
attr_accessor :gitea_data
def initialize(project, id, token=nil)
@project = project
@id = id
@owner = project&.owner.login
@repo = project&.identifier
@token = token
end
def call
load_gitea_data
gitea_data
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
private
def request_params
{
access_token: token
}
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_hooks_by_owner_repo_id(owner, repo, id, {query: request_params}) rescue nil
end
end

View File

@ -0,0 +1,35 @@
class Api::V1::Projects::Webhooks::ListService < ApplicationService
attr_reader :project, :id, :token, :owner, :repo
attr_accessor :gitea_data
def initialize(project, id, token=nil)
@project = project
@id = id
@owner = project&.owner.login
@repo = project&.identifier
@token = token
end
def call
$gitea_client.token = token unless token.blank?
load_gitea_data
$gitea_client.token = nil unless token.blank?
gitea_data
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
private
def request_params
{
access_token: token
}
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_hooks_hooktasks_by_owner_repo(owner, repo, id, {query: request_params}) rescue nil
end
end

View File

@ -0,0 +1,31 @@
class Api::V1::Projects::Webhooks::ListService < ApplicationService
attr_reader :project, :token, :owner, :repo
attr_accessor :gitea_data
def initialize(project, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@token = token
end
def call
load_gitea_data
gitea_data
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
private
def request_params
{
access_token: token
}
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_hooks_by_owner_repo(owner, repo, {query: request_params}) rescue nil
end
end

View File

@ -0,0 +1,32 @@
class Api::V1::Projects::Webhooks::TestsService < ApplicationService
attr_reader :project, :id, :token, :owner, :repo
attr_accessor :gitea_data
def initialize(project, id, token=nil)
@project = project
@id = id
@owner = project&.owner.login
@repo = project&.identifier
@token = token
end
def call
excute_data_to_gitea
gitea_data
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
private
def request_params
{
access_token: token
}
end
def excute_data_to_gitea
@gitea_data = $gitea_client.post_repos_hooks_tests_by_owner_repo_id(owner, repo, id, {query: request_params}) rescue nil
end
end

View File

@ -0,0 +1,63 @@
class Api::V1::Projects::Webhooks::UpdateService < ApplicationService
include ActiveModel::Model
attr_reader :project, :id, :token, :owner, :repo, :active, :branch_filter, :content_type, :url, :http_method, :secret, :events
attr_accessor :gitea_data
validates :url, format: { with: URI::regexp(%w[http https]), message: "请输入正确的地址" }
validates :active, inclusion: {in: [true, false]}
validates :http_method, inclusion: { in: %w(POST GET), message: "请输入正确的请求方式"}
validates :content_type, inclusion: { in: %w(json form), message: "请输入正确的Content Type"}
def initialize(project, id, params, token=nil)
@project = project
@id = id
@owner = project&.owner.login
@repo = project&.identifier
@active = params[:active]
@branch_filter = params[:branch_filter]
@content_type = params[:content_type]
@url = params[:url]
@http_method = params[:http_method]
@secret = params[:secret]
@events = params[:events]
@token = token
end
def call
raise Error, errors.full_messages.join(",") unless valid?
begin
excute_data_to_gitea
gitea_data
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
end
private
def request_params
{
access_token: token
}
end
def request_body
{
active: active,
branch_filter: branch_filter,
config: {
content_type: content_type,
url: url,
http_method: http_method,
secret: secret
},
events: events || [],
type: 'gitea',
}
end
def excute_data_to_gitea
@gitea_data = $gitea_client.patch_repos_hooks_by_owner_repo_id(owner, repo, id, {body: request_body.to_json, query: request_params}) rescue nil
end
end

View File

@ -0,0 +1,86 @@
class Api::V1::Users::Projects::ListService < ApplicationService
include ActiveModel::Model
attr_reader :observe_user, :category, :is_public, :project_type, :sort_by, :sort_direction, :search, :current_user
attr_accessor :queried_projects
validates :category, inclusion: {in: %w(all join created manage watched forked), message: "请输入正确的Category"}
validates :is_public, inclusion: {in: [true, false], message: '请输入正确的IsPublic'}, allow_nil: true
validates :project_type, inclusion: {in: %w(common mirror sync_mirror), message: '请输入正确的ProjectType'}, allow_nil: true
validates :sort_by, inclusion: {in: Project.column_names, message: '请输入正确的SortBy'}
validates :sort_direction, inclusion: {in: %w(asc desc), message: '请输入正确的SortDirection'}
def initialize(observe_user, params, current_user=nil)
@observe_user = observe_user
@category = params[:category] || 'all'
@is_public = params[:is_public]
@project_type = params[:project_type]
@sort_by = params[:sort_by] || 'updated_on'
@sort_direction = params[:sort_direction] || 'desc'
@search = params[:search]
@current_user = current_user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
begin
project_query_data
queried_projects
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
end
private
def project_query_data
if current_user.admin?
projects = Project
else
projects = Project.visible
end
case category
when 'join'
normal_projects = projects.where.not(user_id: observe_user.id).members_projects(observe_user.id).to_sql
org_projects = projects.joins(team_projects: [team: :team_users]).where(team_users: {user_id: observe_user.id}).to_sql
projects = Project.from("( #{normal_projects} UNION #{org_projects} ) AS projects").distinct
when 'created'
projects = projects.where(user_id: observe_user.id)
when 'manage'
normal_projects = projects.joins(members: :roles).where(members: {user_id: observe_user.id}, roles: {name: 'Manager'}).to_sql
org_projects = projects.joins(team_projects: [team: :team_users]).where(team_users: {user_id: observe_user.id}, teams: {authorize: %w(owner admin)}).to_sql
projects = Project.from("( #{normal_projects} UNION #{org_projects} ) AS projects").distinct
when 'watched'
projects = projects.where.not(user_id: observe_user.id).joins(:watchers).where(watchers: {watchable_type: "Project", user_id: observe_user.id})
when 'forked'
fork_ids = observe_user.fork_users.select(:id, :fork_project_id).pluck(:fork_project_id)
projects = projects.where(id: fork_ids)
else
normal_projects = projects.members_projects(observe_user.id).to_sql
org_projects = projects.joins(team_projects: [team: :team_users]).where(team_users: {user_id: observe_user.id}).to_sql
projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
end
unless is_public.nil?
if is_public
projects = projects.visible
else
projects = projects.is_private
end
end
projects = projects.with_project_type(project_type)
q = projects.ransack(name_or_identifier_cont: search)
scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users)
scope = scope.order("projects.#{sort_by} #{sort_direction}")
@queried_projects = scope
end
end

View File

@ -81,7 +81,7 @@ class Educoder::ClientService < ApplicationService
end
def access_key_secret
Gitea.gitea_config[:access_key_secret]
GiteaService.gitea_config[:access_key_secret]
end
def api_url(url)

View File

@ -56,7 +56,7 @@ class Gitea::Accelerator::BaseService < ApplicationService
end
def accelerator
Gitea.gitea_config[:accelerator]
GiteaService.gitea_config[:accelerator]
end
def render_status(response)

View File

@ -96,11 +96,11 @@ class Gitea::ClientService < ApplicationService
end
def base_url
Gitea.gitea_config[:base_url]
GiteaService.gitea_config[:base_url]
end
def domain
Gitea.gitea_config[:domain]
GiteaService.gitea_config[:domain]
end
def api_url

View File

@ -14,8 +14,8 @@ class Gitea::User::DeleteService < Gitea::ClientService
private
def token
{
username: Gitea.gitea_config[:access_key_id],
password: Gitea.gitea_config[:access_key_secret]
username: GiteaService.gitea_config[:access_key_id],
password: GiteaService.gitea_config[:access_key_secret]
}
end

View File

@ -17,7 +17,7 @@ class Gitea::User::UpdateService < Gitea::ClientService
# source_id integer($int64)
# website string
def initialize(edit_username, params={}, token={username: Gitea.gitea_config[:access_key_id], password: Gitea.gitea_config[:access_key_secret]})
def initialize(edit_username, params={}, token={username: GiteaService.gitea_config[:access_key_id], password: GiteaService.gitea_config[:access_key_secret]})
@token = token
@params = params
@edit_username = edit_username

View File

@ -0,0 +1,86 @@
class Projects::LinkJoinService < ApplicationService
Error = Class.new(StandardError)
attr_reader :user, :project, :invite_sign, :params
def initialize(user, project, invite_sign, params={})
@user = user
@project = project
@invite_sign = invite_sign
@params = params
end
def call
ActiveRecord::Base.transaction do
validate!
if invite_link.is_apply
# 如果需要申请才能加入,创建一条申请记录
create_applied_project!
else
# 如果不需要申请,直接为项目添加该成员
create_member!
end
end
end
private
def validate!
raise Error, 'invite_sign必须存在!' if invite_sign.blank?
raise Error, '邀请链接不存在!' unless invite_link.present?
raise Error, '邀请链接已失效!' unless invite_user_in_project
raise Error, '您已是仓库成员' if project.member?(user.id)
raise Error, '您的申请管理员正在审核中,请勿重复申请!' if user.applied_projects.exists?(applied_project_params)
end
def applied_project_params
{
status: 'common',
project: project,
role: role_value,
project_invite_link_id: invite_link&.id
}
end
def create_applied_project!
user.applied_projects.find_or_create_by!(status: 'common', project: project, role: role_value, project_invite_link_id: invite_link&.id)
end
def create_member!
Projects::AddMemberInteractor.call(project.owner, project, user, permission)
end
def invite_link
ProjectInviteLink.find_by(project_id: project.id, sign: invite_sign)
end
def invite_user_in_project
in_project = project.member?(invite_link.user)
invite_link.update_column(:expired_at, Time.now) unless in_project
in_project
end
def role_value
@_role ||=
case invite_link&.role
when 'manager' then 3
when 'developer' then 4
when 'reporter' then 5
else
5
end
end
def permission
case invite_link&.role
when 'manager'
'admin'
when 'developer'
'write'
when 'reporter'
'read'
else
'read'
end
end
end

View File

@ -63,7 +63,7 @@ class Repositories::CreateService < ApplicationService
end
def remote_repository_url
[Gitea.gitea_config[:domain], '/', user.login, '/', params[:identifier], ".git"].join("")
[GiteaService.gitea_config[:domain], '/', user.login, '/', params[:identifier], ".git"].join("")
end
def repository_params

View File

@ -1,6 +1,7 @@
# 代码溯源 导出pdf
require 'open-uri'
require 'fileutils'
require 'zip'
class Trace::PdfReportService < Trace::ClientService
@ -15,8 +16,16 @@ class Trace::PdfReportService < Trace::ClientService
content = open("#{domain}#{base_url}#{url}?task_id=#{task_id}", "Authorization" => token)
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"}
IO.copy_stream(content, "#{save_path}/report.zip")
Zip::File.open("#{save_path}/report.zip") do |zip_file|
zip_file.each do |f|
name = f.name.force_encoding('utf-8')
next unless name == '/report.pdf'
fpath = File.join(save_path, name)
zip_file.extract(f, fpath)
end
end
return {code: 200, download_url: "/trace_task_results/#{task_id}/report.pdf"}
else
return {code: 404}
end
@ -28,7 +37,7 @@ class Trace::PdfReportService < Trace::ClientService
end
def save_path
"public/trace_task_results"
"public/trace_task_results/#{task_id}"
end
def url

View File

@ -2,217 +2,57 @@
<% add_admin_breadcrumb('概览', admins_path) %>
<% end %>
<div class="header bg-gradient-primary pb-8 pt-md-8">
<div class="container-fluid">
<div class="header-body">
<!-- Card stats -->
<div class="row">
<div class="col-xl-3 col-lg-6">
<div class="card card-stats mb-4 mb-xl-0">
<div class="card-body">
<div class="row">
<div class="col">
<h5 class="card-title text-uppercase text-muted mb-0">当日活跃用户</h5>
<span class="h2 font-weight-bold mb-0"><%= @active_user_count %></span>
</div>
<div class="col-auto">
<div class="icon icon-shape rounded-circle shadow">
<i class="fa fa-users"></i>
</div>
</div>
</div>
<!-- <p class="mt-3 mb-0 text-muted text-sm">-->
<!-- <span class="text-success mr-2"><i class="fa fa-arrow-up"></i> 3.48%</span>-->
<!-- <span class="text-nowrap">Since last month</span>-->
<!-- </p>-->
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6">
<div class="card card-stats mb-4 mb-xl-0">
<div class="card-body">
<div class="row">
<div class="col">
<h5 class="card-title text-uppercase text-muted mb-0">7天内活跃用户数</h5>
<span class="h2 font-weight-bold mb-0"><%= @weekly_active_user_count %></span>
</div>
<div class="col-auto">
<div class="icon icon-shape rounded-circle shadow">
<i class="fa fa-users"></i>
</div>
</div>
</div>
<!-- <p class="mt-3 mb-0 text-muted text-sm">-->
<!-- <span class="text-danger mr-2"><i class="fas fa-arrow-down"></i> 3.48%</span>-->
<!-- <span class="text-nowrap">Since last week</span>-->
<!-- </p>-->
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6">
<div class="card card-stats mb-4 mb-xl-0">
<div class="card-body">
<div class="row">
<div class="col">
<h5 class="card-title text-uppercase text-muted mb-0">30天内活跃用户数</h5>
<span class="h2 font-weight-bold mb-0"><%= @month_active_user_count %></span>
</div>
<div class="col-auto">
<div class="icon icon-shape rounded-circle shadow">
<i class="fa fa-users"></i>
</div>
</div>
</div>
<!-- <p class="mt-3 mb-0 text-muted text-sm">-->
<!-- <span class="text-warning mr-2"><i class="fas fa-arrow-down"></i> 1.10%</span>-->
<!-- <span class="text-nowrap">Since yesterday</span>-->
<!-- </p>-->
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6">
<div class="card card-stats mb-4 mb-xl-0">
<div class="card-body">
<div class="row">
<div class="col">
<h5 class="card-title text-uppercase text-muted mb-0">30天内新增用户数</h5>
<span class="h2 font-weight-bold mb-0"><%= @new_user_count %></span>
</div>
<div class="col-auto">
<div class="icon icon-shape rounded-circle shadow">
<i class="fa fa-user-plus"></i>
</div>
</div>
</div>
<!-- <p class="mt-3 mb-0 text-muted text-sm">-->
<!-- <span class="text-success mr-2"><i class="fas fa-arrow-up"></i> 12%</span>-->
<!-- <span class="text-nowrap">Since last month</span>-->
<!-- </p>-->
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box admin-list-container project-language-list-container">
<table class="table table-hover text-center subject-list-table">
<thead class="thead-light">
<tr>
<th width="20%">指标名称</th>
<th width="20%">当日数</th>
<th width="20%">七日内</th>
<th width="20%">30日内</th>
<th width="20%">周用户留存率</th>
</tr>
</thead>
<tbody>
<tr class="">
<td>
<button type="button" class="btn btn-primary" style="font-size:14px;width:85px;padding: 0; height:36px;background-color:#5475fb;border-radius:4px;border-color: #5475fb;">活跃用户</button>
</td>
<td><%=@active_user_count %></td>
<td><%=@weekly_active_user_count %></td>
<td><%=@month_active_user_count %></td>
<td><%="#{@weekly_keep_rate.to_f * 100 }%" %></td>
</td>
</tr>
<tr>
<td>
<button type="button" class="btn btn-primary" style="font-size:14px;width:85px;padding: 0; height:36px;background-color:#36cfc9;border-radius:4px; border-color: #36cfc9;">新注册用户</button>
</td>
<td><%=@day_new_user_count %></td>
<td><%=@weekly_new_user_count %></td>
<td><%=@month_new_user_count %></td>
<td><%="--" %></td>
</tr>
<tr>
<td>
<button type="button" class="btn btn-primary" style="font-size:14px;width:85px;padding: 0; height:36px;background-color:#f89422;border-radius:4px; border-color: #f89422;">活跃项目</button>
</td>
<td><%=@day_active_project_count %></td>
<td><%=@weekly_active_project_count %></td>
<td><%=@month_active_project_count %></td>
<td><%="--" %></td>
</tr>
<tr>
<td>
<button type="button" class="btn btn-primary" style="font-size:14px;width:85px;padding: 0; height:36px;background-color:#9d74ff;border-radius:4px; border-color: #9d74ff;">新增项目</button>
</td>
<td><%=@day_new_project_count %></td>
<td><%=@weekly_new_project_count %></td>
<td><%=@month_new_project_count %></td>
<td><%="--" %></td>
</tr>
</tbody>
</table>
</div>
<div class="container-fluid mt--7 pie-statistic">
<div class="row mt-5">
<div class="col-xl-8 mb-5 mb-xl-0">
<div class="card shadow">
<!-- <div class="card-header border-0">-->
<!-- <div class="row align-items-center">-->
<!-- <h5 class="mb-0">近7天评测次数</h5>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="table-responsive">-->
<!-- <div id="evaluate-pie" class="pie"></div>-->
<!-- </div>-->
</div>
</div>
<div class="col-xl-4">
<div class="card shadow">
<div class="card-header border-0">
<div class="row align-items-center">
<h5 class="mb-0">30天内新增用户</h5>
</div>
</div>
<div class="table-responsive">
<div id="month-active-user" class="pie"></div>
</div>
</div>
</div>
</div>
</div>
<!--<div class="container-fluid mt--7">-->
<!-- <div class="row mt-5">-->
<!-- <div class="col-xl-8 mb-5 mb-xl-0">-->
<!-- <div class="card shadow">-->
<!-- <div class="card-header border-0">-->
<!-- <div class="row align-items-center">-->
<!-- <div class="col">-->
<!-- <h3 class="mb-0">Page visits</h3>-->
<!-- </div>-->
<!-- <div class="col text-right">-->
<!-- <a href="#!" class="btn btn-sm btn-primary">Test</a>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="table-responsive">-->
<!-- <table class="table align-items-center table-flush">-->
<!-- <thead class="thead-light">-->
<!-- <tr>-->
<!-- <th scope="col">Test</th>-->
<!-- <th scope="col">Test</th>-->
<!-- <th scope="col">Test</th>-->
<!-- <th scope="col">Test</th>-->
<!-- </tr>-->
<!-- </thead>-->
<!-- <tbody>-->
<%# 5.times do %>
<!-- <tr>-->
<!-- <th scope="row">/test/</th>-->
<!-- <td>4,569</td>-->
<!-- <td>340</td>-->
<!-- <td>-->
<!-- <i class="fas fa-arrow-up text-success mr-3"></i> 46,53%-->
<!-- </td>-->
<!-- </tr>-->
<%# end %>
<!-- </tbody>-->
<!-- </table>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="col-xl-4">-->
<!-- <div class="card shadow">-->
<!-- <div class="card-header border-0">-->
<!-- <div class="row align-items-center">-->
<!-- <div class="col">-->
<!-- <h3 class="mb-0">Test</h3>-->
<!-- </div>-->
<!-- <div class="col text-right">-->
<!-- <a href="#!" class="btn btn-sm btn-primary">Test</a>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="table-responsive">-->
<!-- <table class="table align-items-center table-flush">-->
<!-- <thead class="thead-light">-->
<!-- <tr>-->
<!-- <th scope="col">Test</th>-->
<!-- <th scope="col">Test</th>-->
<!-- <th scope="col"></th>-->
<!-- </tr>-->
<!-- </thead>-->
<!-- <tbody>-->
<%# 5.times do %>
<!-- <tr>-->
<!-- <th scope="row">-->
<!-- Test-->
<!-- </th>-->
<!-- <td>-->
<!-- 1,480-->
<!-- </td>-->
<!-- <td>-->
<!-- <div class="d-flex align-items-center">-->
<!-- <span class="mr-2">60%</span>-->
<!-- <div>-->
<!-- <div class="progress">-->
<!-- <div class="progress-bar bg-gradient-danger" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 60%;"></div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </td>-->
<!-- </tr>-->
<%# end %>
<!-- </tbody>-->
<!-- </table>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!--</div>-->
<div id="project-language-modals">
</div>

View File

@ -0,0 +1,10 @@
if project.present?
json.type project.project_type
json.(project,
:description, :forked_count, :forked_from_project_id, :identifier,
:issues_count, :pull_requests_count, :invite_code, :website, :platform,
:name, :open_devops, :praises_count, :is_public, :status, :watchers_count,
:ignore_id, :license_id, :project_category_id, :project_language_id)
else
json.nil!
end

View File

@ -0,0 +1,40 @@
json.file_nums diff['NumFiles']
json.total_addition diff['TotalAddition']
json.total_deletion diff['TotalDeletion']
json.files diff['Files'].each do |file|
json.name file['Name']
json.oldname file['OldName']
json.addition file['Addition']
json.deletion file['Deletion']
json.type file['Type']
json.is_created file['IsCreated']
json.is_deleted file['IsDeleted']
json.is_bin file['IsBin']
json.is_lfs_file file['IsLFSFile']
json.is_renamed file['IsRenamed']
json.is_ambiguous file['IsAmbiguous']
json.is_submodule file['IsSubmodule']
json.sections file['Sections'] do |section|
json.file_name section['FileName']
json.name section['Name']
json.lines section['Lines'] do |line|
json.left_index line['LeftIdx']
json.right_index line['RightIdx']
json.match line['Match']
json.type line['Type']
json.content line['Content']
unless line['SectionInfo'].blank?
json.section_path line['SectionInfo']['Path']
json.section_last_left_index line['SectionInfo']['LastLeftIdx']
json.section_last_right_index line['SectionInfo']['LastRightIdx']
json.section_left_index line['SectionInfo']['LeftIdx']
json.section_right_index line['SectionInfo']['RightIdx']
json.section_left_hunk_size line['SectionInfo']['LeftHunkSize']
json.section_right_hunk_size line['SectionInfo']['RightHunkSize']
end
end
end
json.is_incomplete file['IsIncomplete']
json.is_incomplete_line_too_long file['IsIncompleteLineTooLong']
json.is_protected file['IsProtected']
end

View File

@ -0,0 +1,22 @@
json.file_size @result_object['file_size']
json.file_name @result_object['file_name']
json.num_lines @result_object['num_lines']
json.blame_parts @result_object['blame_parts'] do |part|
json.commit do
json.sha part['commit']['id']
json.author do
json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(part['commit']['author']), name: part['commit']['author']['Name'] }
end
json.committer do
json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(part['commit']['commiter']), name: part['commit']['commiter']['Name'] }
end
json.commit_message part['commit']['commit_message']
json.authored_time render_unix_time(part['commit']['authored_time'])
json.committed_time render_unix_time(part['commit']['committed_time'])
json.created_time render_unix_time(part['commit']['created_time'])
end
json.current_number part['current_number']
json.effect_line part['effect_line']
json.lines part['lines']
end

View File

@ -0,0 +1,4 @@
json.name branch
json.http_url render_http_url(@project)
json.zip_url render_zip_url(@owner, @project.repository, branch)
json.tar_url render_tar_url(@owner, @project.repository, branch)

View File

@ -0,0 +1,26 @@
json.name branch['name']
json.commit do
json.id branch['commit']['id']
json.message branch['commit']['message']
json.author do
json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(branch['commit']['author']), name: branch['commit']['author']['name'] }
end
json.committer do
json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(branch['commit']['committer']), name: branch['commit']['committer']['name'] }
end
json.time_ago time_from_now(branch['commit']['timestamp'].to_time)
json.timestamp branch['commit']['timestamp']
end
json.protected branch['protected']
json.user_can_push branch['user_can_push']
json.user_can_merge branch['user_can_merge']
json.commit_id branch['commit_id']
json.commit_time_from_now time_from_now(branch['commit_time'].to_time)
json.commit_time branch['commit_time']
json.default_branch branch['default_branch']
json.http_url render_http_url(@project)
json.zip_url render_zip_url(@owner, @project.repository, branch['name'])
json.tar_url render_tar_url(@owner, @project.repository, branch['name'])

View File

@ -0,0 +1,3 @@
json.array! @result_object["branch_name"] do |branch|
json.partial! "api/v1/projects/branches/simple_detail", branch: branch
end

View File

@ -0,0 +1 @@
json.partial! "api/v1/projects/branches/simple_gitea_detail", branch: @result_object

View File

@ -0,0 +1,10 @@
json.sha commit['sha']
json.author do
json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(commit['author']), name: commit['author']['name'] }
end
json.committer do
json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(commit['committer']), name: commit['committer']['name'] }
end
json.commit_message commit['message']
json.parent_shas commit['parents'].map{|x|x['sha']}

View File

@ -0,0 +1 @@
json.partial! "api/v1/projects/simple_gitea_diff_detail", diff: @result_object

View File

@ -0,0 +1,17 @@
json.total_count @result_object[:total_data].to_i
json.commits @result_object[:data].each do |commit|
json.sha commit['sha']
json.author do
json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(commit['commit']['author']), name: commit['commit']['author']['name'] }
end
json.committer do
json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(commit['commit']['committer']), name: commit['commit']['committer']['name'] }
end
json.commit_message commit['commit']['message']
json.parent_shas commit['parents'].map{|x|x['sha']}
json.files commit['files'].map{|f|f['filename']}
json.commit_date commit['commit_date']
json.commit_time render_unix_time(commit['commit']['committer']['date'])
json.branch commit['branch']
end

View File

@ -0,0 +1,18 @@
json.commits_count @result_object['CommitsCount']
json.last_commit_sha @result_object['LatestSha']
json.commits @result_object['Commits'] do |commit|
json.author do
json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(commit['Author']), name: commit['Author']['Name'] }
end
json.committer do
json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] }
end
json.branch commit['Branch']
json.commit_message commit['CommitMessage']
json.sha commit['Sha']
json.parent_shas commit['Sha']['ParentShas']
end
json.diff do
json.partial! "api/v1/projects/simple_gitea_diff_detail", diff: @result_object['Diff']
end

View File

@ -0,0 +1,14 @@
json.commit do
json.partial! "api/v1/projects/commits/simple_gitea_detail", commit: @result_object['commit']
json.authored_time render_unix_time(@result_object['commit']['author']['date'])
json.commited_time render_unix_time(@result_object['commit']['committer']['date'])
end
json.contents @result_object['contents'].each do |content|
json.name content['name']
json.path content['path']
json.sha content['sha']
json.type content['type']
json.size content['size']
json.encoding content['encoding']
json.content content['content']
end

View File

@ -0,0 +1,4 @@
json.sha @result_object['sha']
json.size @result_object['size']
json.encoding @result_object['encoding']
json.content @result_object['content']

View File

@ -0,0 +1,9 @@
json.total_count @result_object['total_count']
json.sha @result_object['sha']
json.entries @result_object['tree'].each do |entry|
json.name entry['path']
json.mode entry['mode']
json.type entry['type'] === 'blob' ? 'file' : 'dir'
json.size entry['size']
json.sha entry['sha']
end

View File

@ -0,0 +1,5 @@
json.owner do
json.partial! "api/v1/users/simple_user", user: @result_object[:owner]
end
json.(@result_object, :full_name, :ssh_url, :clone_url, :default_branch, :empty, :branch_count, :tag_count)
json.partial! "api/v1/projects/simple_detail", project: @project

View File

@ -0,0 +1,3 @@
json.(webhook, :id, :url, :http_method, :is_active)
json.last_status webhook.last_status
json.create_time Time.at(webhook.created_unix).strftime("%Y-%m-%d %H:%M:%S")

View File

@ -0,0 +1,8 @@
json.id webhook["id"]
json.content_type webhook['config']['content_type']
json.http_method webhook['config']['http_method']
json.url webhook['config']['url']
json.events webhook['events']
json.active webhook['active']
json.branch_filter webhook['branch_filter']
json.created_at format_time(webhook['created_at'].to_time)

View File

@ -0,0 +1 @@
json.partial! "api/v1/projects/webhooks/simple_gitea_detail", webhook: @result_object

View File

@ -0,0 +1,6 @@
json.total_count @hooktasks.total_count
json.hooktasks @hooktasks.each do |task|
json.(task, :id, :event_type, :type, :uuid, :is_succeed, :is_delivered, :payload_content, :request_content)
json.response_content task.response_content_json
json.delivered_time Time.at(task.delivered*10**-9).strftime("%Y-%m-%d %H:%M:%S")
end

View File

@ -0,0 +1,4 @@
json.total_count @webhooks.total_count
json.webhooks @webhooks do |webhook|
json.partial! "api/v1/projects/webhooks/simple_detail", webhook: webhook
end

View File

@ -0,0 +1 @@
json.partial! "api/v1/projects/webhooks/simple_gitea_detail", webhook: @result_object

View File

@ -0,0 +1 @@
json.partial! "api/v1/projects/webhooks/simple_gitea_detail", webhook: @result_object

View File

@ -0,0 +1,21 @@
if user.present?
if user.is_a?(Hash)
json.id user["id"]
json.login user["login"]
json.name user["name"]
json.type user["type"]
json.image_url user["avatar_url"]
else
json.id user.id
json.login user.login
json.name user.real_name
json.type user&.type
json.image_url url_to_avatar(user)
end
else
json.id nil
json.login name
json.name name
json.type nil
json.image_url User::Avatar.get_letter_avatar_url(name)
end

View File

@ -0,0 +1,9 @@
if user.present?
json.id user.id
json.type user.type
json.name user.real_name
json.login user.login
json.image_url url_to_avatar(user)
else
json.nil!
end

Some files were not shown because too many files have changed in this diff Show More