Compare commits

...

117 Commits

Author SHA1 Message Date
jasder e638dccfa5 update readme 2021-03-31 16:08:00 +08:00
jasder b89501822f FIX code bug 2021-03-31 14:53:50 +08:00
jasder 5f5b380514 FIX conflict 2021-03-31 13:35:40 +08:00
jasder d2293a2778 FIX setting api bug 2021-03-31 12:44:40 +08:00
jasder 7d0b05f975 update readme 2021-03-31 12:43:06 +08:00
jasder 9393ceb8b7 Update readme and deploy 2021-03-31 10:30:39 +08:00
jasder 860b46685a fix bug 2021-03-31 10:24:37 +08:00
jasder 62b99c517e Update deploy doc 2021-03-31 10:08:20 +08:00
jasder 3869529620 Update setting api 2021-03-31 09:49:05 +08:00
vilet.yy 9394d15752 fix: menu manager allow visit 2021-03-30 17:27:50 +08:00
vilet.yy 8952f6a944 fix: menu manager allow visit 2021-03-30 17:13:58 +08:00
vilet.yy ffa2782637 fix: detail release version limit 2021-03-30 16:34:31 +08:00
vilet.yy d0a65775aa fix: not show commiter avatar 2021-03-30 16:34:12 +08:00
vilet.yy d7a459bad2 add: dir md 2021-03-30 16:33:21 +08:00
jasder fc522be5ea Merge branch 'trustie_server' into develop 2021-03-30 15:25:59 +08:00
jasder 9195d43a34 Update readme and deply doc 2021-03-30 15:15:17 +08:00
jasder a866415990 Merge branch 'master' into develop 2021-03-30 14:20:47 +08:00
jasder 69bf30d482 Merge branch 'develop' into trustie_server 2021-03-28 20:05:34 +08:00
jasder c4560ebb66 FIX tags api bug 2021-03-28 20:05:13 +08:00
jasder 0211d6152e fix conflict 2021-03-28 20:00:28 +08:00
jasder 12f1aba07e Merge branch 'develop' into trustie_server 2021-03-28 19:44:36 +08:00
jasder 63d91919ae FIX repo detail api bug 2021-03-28 19:44:23 +08:00
jasder 06c851d22f Merge branch 'develop' into trustie_server 2021-03-27 23:54:32 +08:00
jasder ad4617288d FIX repositories detail api bug 2021-03-27 23:54:14 +08:00
jasder 7c0a2aa89e Merge branch 'develop' into trustie_server 2021-03-25 14:31:45 +08:00
jasder e1d7b0b881 FIX readme decoder bug
safe
2021-03-25 14:30:26 +08:00
vilet.yy 7f28f07665 fix: readme error 2021-03-25 11:31:43 +08:00
jasder 3be5c013a8 fix bug 2021-03-24 18:36:05 +08:00
vilet.yy 70c7b1da0c add: auto counter rake 2021-03-24 15:42:57 +08:00
vilet.yy 66608b3baa add: detail project description 2021-03-24 13:59:24 +08:00
vilet.yy 9ee42e805b fix: update error 2021-03-24 13:50:17 +08:00
vilet.yy 15f9004f63 add: edit permit website 2021-03-24 10:50:47 +08:00
jasder 0f67944911 FIX code bug 2021-03-23 15:58:12 +08:00
jasder 8b656fd409 FIX bug 2021-03-23 15:47:01 +08:00
jasder 815b364428 ADD common url with setting api 2021-03-23 15:42:56 +08:00
jasder 18ae3853d7 FIX update setting api 2021-03-23 11:42:22 +08:00
jasder ce18cc380a Merge branch 'develop' into trustie_server 2021-03-22 21:02:17 +08:00
jasder cbc6f4e7d1 fix bug 2021-03-22 20:53:39 +08:00
jasder aa23762b2f FIX update setting api for nav bar url 2021-03-22 18:02:12 +08:00
vilet.yy 5c864acb64 fix: create repo not use website 2021-03-22 15:50:08 +08:00
vilet.yy 8bbfea2079 fix 2021-03-22 11:18:04 +08:00
vilet.yy 8c2a543e61 fix: project code change to default 2021-03-22 11:16:35 +08:00
jasder 2932866198 Merge branch 'master' of https://git.trustie.net/jasder/forgeplus 2021-03-22 09:50:35 +08:00
jasder 0d8ddc34cc FIX update Changelog 2021-03-22 09:50:24 +08:00
jasder 0909223867 FIX get readme api method 2021-03-21 11:24:34 +08:00
jasder c07c570061 动态导航权限控制,没有勾选模块不能进入页面,语言过滤0.0%
动态导航权限控制,没有勾选模块不能进入页面,语言过滤0.0%
2021-03-19 18:40:23 +08:00
vilet.yy 223755a965 fix: languages filter 0.0% 2021-03-19 18:36:55 +08:00
vilet.yy f1ed61c687 add: menu permission control 2021-03-19 18:01:29 +08:00
vilet.yy 553a584121 fix: migrate create unit
fix
2021-03-19 17:38:14 +08:00
jasder 04288a734e 木兰项目同步脚本以及动态导航简化 2021-03-19 15:07:07 +08:00
vilet.yy db2ba95e24 fadd: repo edit unit 2021-03-19 15:05:09 +08:00
vilet.yy 39f0811f0a fix:simple unit 2021-03-19 14:58:25 +08:00
vilet.yy 495de925dd add: sync mulan repo 2021-03-19 11:29:31 +08:00
jasder f18c56129e FIX 改版项目详情页面 2021-03-18 18:08:38 +08:00
vilet.yy 6a7ef2a1a7 add: repo contributors 2021-03-18 16:35:46 +08:00
vilet.yy 25186b4e81 fix 2021-03-18 09:55:58 +08:00
vilet.yy a627ab4d82 fix 2021-03-18 09:51:56 +08:00
vilet.yy 187ad1d263 fix 2021-03-17 13:51:08 +08:00
vilet.yy 8f98a83379 add: project unit list 2021-03-17 13:50:26 +08:00
vilet.yy a14459da50 add: project menu list 2021-03-17 11:52:36 +08:00
vilet.yy f17e436d62 add: doc 2021-03-16 11:10:58 +08:00
vilet.yy 34d95451b5 add: get project all files 2021-03-16 10:25:37 +08:00
vilet.yy b43926f69f add: new project detail 2021-03-15 19:01:35 +08:00
jasder bd46d2dcc7 fix 2021-03-11 14:59:21 +08:00
jasder 6063c7c414 fix 更新api文档 2021-03-11 14:58:44 +08:00
jasder af289d1f5e Update changelog 2021-03-05 15:52:50 +08:00
jasder 899e7ccab8 FIX CI 修复参数设置的bug 2021-03-05 15:48:08 +08:00
victor d3df04fb97 参数管理修改 2021-03-05 15:43:42 +08:00
jasder 50928f82c0 FIX valid user before register action 2021-03-05 15:36:26 +08:00
victor 33984e16d0 参数管理修改 2021-03-05 15:26:29 +08:00
jasder 8942c694c4 Merge branch 'develop' of https://git.trustie.net/jasder/forgeplus into develop 2021-03-05 11:38:45 +08:00
jasder d3a3c645c8 集成CI/CD参数管理功能 2021-03-05 10:48:38 +08:00
victor 3b5d971964 参数删除接口文档 2021-03-04 17:00:29 +08:00
victor 61e5b9132a 参数删除接口修改 2021-03-04 16:52:00 +08:00
victor d7bc309d49 参数管理删除 2021-03-04 09:33:04 +08:00
victor cb90dc1f51 参数管理 2021-03-03 15:36:59 +08:00
jasder 347af1a8ad Merge branch 'develop' of https://git.trustie.net/jasder/forgeplus into develop 2021-03-02 15:21:50 +08:00
jasder 41118f6d12 解决pull创建和更新操作的bug 2021-03-02 10:07:13 +08:00
vilet.yy ccbaa8efe5 [FIX]pull_request create and update error 2021-03-01 16:43:09 +08:00
jasder 11350bb76f Merge branch 'develop' of https://git.trustie.net/jasder/forgeplus into develop 2021-03-01 10:02:35 +08:00
jasder b6b3cb0825 fix bug 2021-02-26 17:51:42 +08:00
jasder fc680b5372 fix notice api 2021-02-26 14:38:32 +08:00
vilet.yy c299c9fa69 [FIX]repository setting entry error 2021-02-26 13:49:29 +08:00
vilet.yy 70922b81db [FIX]watcher type error 2021-02-25 11:52:24 +08:00
vilet.yy 93fa02bd7b [FIX]
[FIX]

[FIX]
2021-02-22 10:53:01 +08:00
vilet.yy ef0c44f29c [ADD]fix error script 2021-02-22 10:10:22 +08:00
vilet.yy 9681a1a491 [FIX]sync mirror project 2021-02-21 17:14:51 +08:00
vilet.yy f7b7122d60 [FIX]join projects add org projects and update project private bug 2021-02-21 15:20:04 +08:00
Gitea 11471b1520 [FIX]transfer need to save clone_url 2021-02-21 10:02:47 +08:00
jasder 50d81b2490 FIX schema 2021-02-09 11:49:47 +08:00
jasder 9f4c2bd673 FIX repo setting api for forbidden 2021-02-09 11:48:05 +08:00
viletyy 2b898b0d1f [FIX]remove description 2021-02-06 16:43:41 +08:00
jasder 8e4d6c7064 合并组织模块功能 2021-02-06 16:21:47 +08:00
viletyy f06a1f63dd [ADD]project transfer
[FIX]

[FIX]
2021-02-06 15:49:59 +08:00
viletyy d85075f951 [FIX]org sync gitea_uid
[FIX]
2021-02-06 15:49:56 +08:00
viletyy ec1acde99a [FIX]show teams, org name valid message 2021-02-06 15:49:56 +08:00
viletyy 55528950f1 [FIX]owner text 2021-02-06 15:49:56 +08:00
viletyy 54204d4db8 [ADD]org team search, project teams add\remove, org can create field
[FIX]
2021-02-06 15:49:55 +08:00
viletyy 4f6f257b37 [FIX]concat change to concat_ws, org render 403, normal team leave 2021-02-06 15:49:54 +08:00
viletyy 629f3fbbe1 [FIX]little vliad about team_user 2021-02-06 15:49:54 +08:00
viletyy ddaca5bafc [ADD]organization add num_teams 2021-02-06 15:49:54 +08:00
viletyy 1e70764d63 [FIX]teams add user info 2021-02-06 15:49:54 +08:00
viletyy 152e301bf3 [ADD]search is_member is_admin 2021-02-06 15:49:54 +08:00
viletyy c6269f44ee [FIX]add user organization count 2021-02-06 15:49:54 +08:00
viletyy 3d495797c0 [FIX]organization add created_at and total_count 2021-02-06 15:49:54 +08:00
viletyy ce4c71c6a0 [FIX]组织可以不上传头像 2021-02-06 15:49:54 +08:00
viletyy dab57e9af3 [FIX]一些筛选以及创建最大项目数量验证 2021-02-06 15:49:53 +08:00
viletyy fef43caf4c [ADD]组织项目相关 2021-02-06 15:49:53 +08:00
viletyy 289d1877d6 [ADD]组织、组织团队 2021-02-06 15:49:53 +08:00
viletyy 0c58447afb [FIX]组织上传头像支持多种格式、项目详情返回拥有者类型
[FIX]

[FIX]
2021-02-06 15:49:52 +08:00
viletyy 3f73484596 [FIX]一些筛选以及创建最大项目数量验证
[FIX]
2021-02-06 15:49:49 +08:00
viletyy f094fe1799 [ADD]组织项目相关 2021-02-06 14:27:47 +08:00
viletyy 1dc43a23b7 [FIX]为组织创建项目做准备 2021-02-06 14:27:47 +08:00
viletyy 31858a79e3 [FIX]成员细节修改 2021-02-06 14:27:47 +08:00
viletyy 7f309edc91 [ADD]组织、组织团队 2021-02-06 14:27:47 +08:00
viletyy c278ed1863 [FIX] 2021-02-06 14:27:47 +08:00
viletyy 2434ca9681 [ADD]组织模块相关代码 2021-02-06 14:27:47 +08:00
150 changed files with 4431 additions and 273 deletions

1
.gitignore vendored
View File

@ -36,6 +36,7 @@ public/react/yarn.lock
/.idea/*
# Ignore react node_modules
public/react/*
/public/react/.cache
/public/react/node_modules/
/public/react/config/stats.json

26
CHANGELOG.md Normal file
View File

@ -0,0 +1,26 @@
# Changelog
## [v3.0.1](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-03-19
* BUGFIXES
* Fix pull reqeust模块中用户头像显示问题
* Fix 解决issue模块中指派用户的问题
* Fix 解决合并请求失败的后显示message信息不正确
* Fix 代码目录页面readme文件提供单独的api
* ENHANCEMENTS
* 重构项目详情页面
## [v3.0.0](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2020-12-29
* BUGFIXES
* Fix pull reqeust的访问权限问题 (#14156) (#14171)
* Fix 部分页面访问403的问题
* Fix 合并请求失败的问题
* Fix 代码目录页面,文件夹和文件图片不显示的问题
* Fix Issue列表中按负责人过滤筛选时部分项目成员不在搜索范围没的问题
* Fix 项目更新时间不同步的问题, 添加了定时任务
* ENHANCEMENTS
* 提升获取版本库目录代码查询速度 (#43296)
* tag 列表慢查询速度问题 (#43332)
* API builder 模版中 render partial builder的性能问题

View File

@ -11,13 +11,32 @@ Trustie (确实)是一个以大众化协同开发、开放式资源共享、
## 部署
### 1. 安装依赖包
### Depends Versions
```bash
bundle install
* Ruby 2.4.5
* Rails ~> 5.2
* MySql ~> 5.6
* Redis 5+
* NodeJS > 13.0.0
### Steps
#### 1. 克隆稳定版本
```
git clone -b standalone https://git.trustie.net/jasder/forgeplus.git
```
### 2. 配置初始化文件
#### 2. 安装依赖包
```bash
cd forgeplus && bundle install
```
#### 3. 配置初始化文件
进入项目根目录执行一下命令:
```bash
@ -27,10 +46,24 @@ touch config/redis.yml
touch config/elasticsearch.yml
```
### 3. 配置gitea服务(可选)
#### 4. 配置数据库
数据库配置信息请查看/config/database.yml文件
项目默认采用mysql数据库, 如需更改,请自行修改配置信息,
默认配置如下:
```bash
default: &default
adapter: mysql2
host: 127.0.0.1
encoding: utf8
username: root
password: 123456
```
#### 5. 配置gitea服务(可选)
**如需要部署自己的gitea平台请参考gitea官方平台https://docs.gitea.io/zh-cn/install-from-binary/**
**因目前gitea平台api受限暂时推荐从forge平台获取gitea部署文件进行部署https://forgeplus.trustie.net/projects/jasder/gitea-binary
**因目前gitea平台api受限暂时推荐从forge平台获取gitea部署文件进行部署https://forgeplus.trustie.net/projects/Trustie/gitea-binary**
#### 配置gitea服务步骤
1. 部署gitea服务并注册root账户
@ -44,75 +77,91 @@ gitea:
base_url: '/api/v1'
```
### 4. 安装redis环境
#### 6. 安装redis环境
**请自行搜索各平台如何安装部署redis环境**
### 5. 创建数据库
#### 7. 创建数据库
**开发环境为development 生成环境为production**
```bash
rails db:create
rails db:create RAILS_ENV=development
```
### 6. 导入数据表结构
#### 8. 导入数据表结构
```bash
bundle exec rake sync_table_structure:import_csv
```
### 7. 执行migrate迁移文件
#### 9. 执行migrate迁移文件
**开发环境为development 生成环境为production**
```bash
rails db:migrate RAILS_ENV=development
```
### 8. 启动redis(此处已mac系统为例)
#### 10. clone前端代码
**将前端代码克隆到public/react目录下目录结构应该是: public/react/build**
```bash
git clone -b standalone https://git.trustie.net/jasder/build.git
```
#### 11. 启动redis(此处已mac系统为例)
```bash
redis-server&
```
### 9. 启动sidekiq
#### 12. 启动sidekiq
**开发环境为development 生成环境为production**
```bash
bundle exec sidekiq -C config/sidekiq.yml -e production -d
```
### 10. 启动rails服务
#### 13. 启动rails服务
```bash
rails s
```
### 11. 浏览器访问
#### 14. 浏览器访问
在浏览器中输入如下地址访问:
```bash
http://localhost:3000/
```
#### 15. 浏览器访问
在浏览器中输入如下地址访问:
```bash
http://localhost:3000/
```
#### 15. 其他说明
通过页面注册都第一个用户为平台管理员用户
## 页面展示
- 代码库
![](docs/figs/code.png)
![](docs/figs/code.png?raw=true)
- 任务管理
![](docs/figs/issue_manage.png)
![](docs/figs/issue_manage.png?raw=true)
- 任务查看
![](docs/figs/issue_view.png)
![](docs/figs/issue_view.png?raw=true)
- 任务指派
![](docs/figs/issue_assign2.png)
![](docs/figs/issue_assign2.png?raw=true)
- 里程碑
![](docs/figs/milestone.png)
![](docs/figs/milestone.png?raw=true)
### API
- [API](api_document.md)
- [API文档](https://forgeplus.trustie.net/docs/api)
- [API](showdoc.com.cn)
账号forgeplus@admin.com 密码forge123
## 贡献代码

View File

@ -1,4 +1,3 @@
---
# API文档
@ -19,7 +18,6 @@
### API接口
---
#### 用户注册(通过其他平台)
```
@ -4830,8 +4828,133 @@ curl --location --request DELETE 'http://localhost:3000/api/ci/templates/10'
------
#### 参数列表查询
```
GET /api/ci/secrets/secrets?owner={owner}&repo={repo}
```
*示例*
```bash
curl --location --request GET 'http://localhost:3000/api/ci/secrets?owner=test&repo=test'
```
*请求参数说明:*
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ---------- |
| owner | 是 | string | 仓库所有人 |
| repo | 是 | string | 仓库名 |
*返回参数说明:*
| 参数名 | 类型 | 说明 |
| ------ | ------ | ------ |
| name | string | 参数名 |
| data | string | 参数值 |
| repo | string | 仓库 |
返回值
```json
[
{
"id": 1,
"name": "test",
"data": "test",
"repo": "test"
}
]
```
------
#### 参数新增/更新
```
POST /api/ci/secrets?owner={owner}&repo={repo}
```
*示例*
```bash
curl --location --request POST 'http://localhost:3000/api/ci/secrets?owner=test&repo=test' \
--data-raw ' {
"name": "ip",
"data": "1.1.1.1",
"id": 21
}'
```
*请求参数说明:*
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------------ |
| owner | 是 | string | 仓库拥有者 |
| repo | 是 | string | 仓库名 |
| name | 是 | string | 参数名 |
| data | 是 | string | 参数值 |
| id | 否 | int | id更新时传 |
*返回参数说明:*
| 参数名 | 类型 | 说明 |
| ------- | ------ | ------------ |
| status | int | 状态码 0成功 |
| message | string | 消息 |
返回值
```json
{
"status": 0,
"message": "success"
}
```
------
#### 参数删除
```
DELETE /api/ci/secrets/{id}?name={name}&owner={owner}&repo={repo}
```
*示例*
```bash
curl --location --request DELETE 'http://localhost:3000/api/ci/secrets/1?name=p1&owner=victor&repo=trustieTest' \
```
*请求参数说明:*
| 参数名 | 必选 | 类型 | 说明 |
| ------ | ---- | ------ | ------ |
| name | 是 | string | 参数名 |
| id | 是 | int | 参数id |
*返回参数说明:*
| 参数名 | 类型 | 说明 |
| ------- | ------ | ------------ |
| status | int | 状态码 0成功 |
| message | string | 返回消息 |
返回值
```json
{
"status": 0,
"message": "success"
}
```
------
#### 解除CI服务器绑定
```

View File

@ -680,6 +680,14 @@ class ApplicationController < ActionController::Base
relation.page(page).per(limit)
end
def kaminari_array_paginate(relation)
limit = params[:limit] || params[:per_page]
limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i
page = params[:page].to_i.zero? ? 1 : params[:page].to_i
Kaminari.paginate_array(relation).page(page).per(limit)
end
def strf_time(time)
time.blank? ? '' : time.strftime("%Y-%m-%d %H:%M:%S")
end

View File

@ -0,0 +1,53 @@
class Ci::SecretsController < Ci::BaseController
before_action :load_repo
# 参数列表
def index
result = Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], nil).secrets
@secrets = result
end
#新增、更新参数
def create
options = {
name: params[:name],
data: params[:data]
}
id = params[:id]
if id
result = Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], options).update_secret
if result["id"]
render_ok
else
render_error(result["message"])
end
else
result = Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], options).create_secret
if result["id"]
render_ok
else
render_error(result["message"])
end
end
end
#删除参数
def destroy
name = params[:name]
if !name.blank?
Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], {name: name}).delete_secret
render_ok
else
render_error("参数名不能为空")
end
rescue Exception => ex
render_ok
end
def ci_drone_url
user = User.find_by(login: params[:owner])
user&.ci_cloud_account.drone_url
end
end

View File

@ -1,12 +1,14 @@
module PaginateHelper
def paginate(objs, **opts)
page = params[:page].to_i <= 0 ? 1 : params[:page].to_i
per_page = params[:per_page].to_i > 0 && params[:per_page].to_i < 50 ? params[:per_page].to_i : opts[:per_page] || 20
if objs.is_a?(Array)
Kaminari.paginate_array(objs).page(page).per(per_page)
def paginate(relation)
limit = params[:limit] || params[:per_page]
limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i
page = params[:page].to_i.zero? ? 1 : params[:page].to_i
if relation.is_a?(Array)
Kaminari.paginate_array(relation).page(page).per(limit)
else
objs.page(page).per(per_page)
relation.page(page).per(limit)
end
end
end

View File

@ -8,6 +8,8 @@ module RegisterHelper
user.password = password
user.platform = platform
user.activate
return unless user.valid?
interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password})
if interactor.success?

View File

@ -14,6 +14,6 @@ module Repository::LanguagesPercentagable
hash.transform_values { |v|
ActionController::Base.helpers
.number_to_percentage((v * 100.0 / total_byte_size), precision: 1)
}
}.select{|k,v| v != "0.0%"}
end
end

View File

@ -12,6 +12,7 @@ class IssuesController < ApplicationController
include TagChosenHelper
def index
return render_not_found unless @project.has_menu_permission("issues")
@user_admin_or_member = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user))
issues = @project.issues.issue_issue.issue_index_includes
issues = issues.where(is_private: false) unless @user_admin_or_member

View File

@ -0,0 +1,34 @@
class Organizations::BaseController < ApplicationController
include ApplicationHelper
include PaginateHelper
protected
def can_edit_org?
current_user.admin? || @organization.is_owner?(current_user.id)
end
def check_user_can_edit_org
tip_exception("您没有权限进行该操作") unless can_edit_org?
end
def org_limited_condition
@organization.organization_extension.limited? && !current_user.logged?
end
def org_privacy_condition
@organization.organization_extension.privacy? && @organization.organization_users.where(user_id: current_user.id).blank?
end
def team_not_found_condition
@team.team_users.where(user_id: current_user.id).blank? && !@organization.is_owner?(current_user.id)
end
def user_mark
params[:username] || params[:id]
end
def project_mark
params[:repo_name] || params[:id]
end
end

View File

@ -0,0 +1,57 @@
class Organizations::OrganizationUsersController < Organizations::BaseController
before_action :load_organization
before_action :load_operate_user, :load_organization_user, :check_user_can_edit_org, only: [:destroy]
def index
@organization_users = @organization.organization_users.includes(:user)
search = params[:search].to_s.downcase
@organization_users = @organization_users.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present?
@organization_users = kaminari_paginate(@organization_users)
end
def destroy
tip_exception("您不能从所有者团队中删除最后一个用户") if @organization.is_owner_team_last_one?(@operate_user.id)
ActiveRecord::Base.transaction do
@organization_user.destroy!
TeamUser.where(organization_id: @organization.id, user_id: @operate_user.id).map{|u| u.destroy!}
Gitea::Organization::OrganizationUser::DeleteService.call(@organization.gitea_token, @organization.login, @operate_user.login)
render_ok
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def quit
@organization_user = @organization.organization_users.find_by(user_id: current_user.id)
tip_exception("您不在该组织中") if @organization_user.nil?
tip_exception("您不能从所有者团队中删除最后一个用户") if @organization.is_owner_team_last_one?(current_user.id)
ActiveRecord::Base.transaction do
@organization_user.destroy!
TeamUser.where(organization_id: @organization.id, user_id: current_user.id).map{|u| u.destroy!}
Gitea::Organization::OrganizationUser::DeleteService.call(@organization.gitea_token, @organization.login, current_user.login)
render_ok
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
private
def load_organization
@organization = Organization.find_by(login: params[:organization_id]) || Organization.find_by(id: params[:organization_id])
return render_not_found("组织不存在") if @organization.nil?
return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition
end
def load_operate_user
@operate_user = User.find_by(login: user_mark) if user_mark.present?
tip_exception("平台用户不存在") if @operate_user.nil?
end
def load_organization_user
@organization_user = OrganizationUser.find_by(organization_id: @organization.id, user_id: @operate_user.id)
tip_exception("组织成员不存在") if @organization_user.nil?
end
end

View File

@ -0,0 +1,106 @@
class Organizations::OrganizationsController < Organizations::BaseController
before_action :require_login, except: [:index, :show]
before_action :convert_image!, only: [:create, :update]
before_action :load_organization, only: [:show, :update, :destroy]
before_action :check_user_can_edit_org, only: [:update, :destroy]
def index
if current_user.logged?
logged_organizations_sql = Organization.with_visibility(%w(common limited)).to_sql
privacy_organizations_sql = Organization.with_visibility("privacy").joins(:organization_users).where(organization_users: {user_id: current_user.id}).to_sql
@organizations = Organization.from("( #{ logged_organizations_sql } UNION #{ privacy_organizations_sql } ) AS users")
else
@organizations = Organization.with_visibility("common")
end
@organizations = @organizations.ransack(login_cont: params[:search]).result if params[:search].present?
@organizations = @organizations.includes(:organization_extension).order("organization_extensions.#{sort_by} #{sort_direction}")
@organizations = kaminari_paginate(@organizations)
end
def show
@can_create_project = @organization.can_create_project?(current_user.id)
@is_admin = can_edit_org?
@is_member = @organization.is_member?(current_user.id)
end
def create
ActiveRecord::Base.transaction do
@organization = Organizations::CreateService.call(current_user, organization_params)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def update
ActiveRecord::Base.transaction do
login = @organization.login
@organization.update!(login: organization_params[:name]) if organization_params[:name].present?
@organization.organization_extension.update_attributes!(organization_params.except(:name))
Gitea::Organization::UpdateService.call(@organization.gitea_token, login, @organization.reload)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def destroy
tip_exception("密码不正确") unless current_user.check_password?(password)
ActiveRecord::Base.transaction do
Gitea::Organization::DeleteService.call(@organization.gitea_token, @organization.login)
@organization.destroy!
end
render_ok
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
private
def convert_image!
return unless params[:image].present?
max_size = EduSetting.get('upload_avatar_max_size') || 2 * 1024 * 1024 # 2M
if params[:image].class == ActionDispatch::Http::UploadedFile
@image = params[:image]
render_error('请上传文件') if @image.size.zero?
render_error('文件大小超过限制') if @image.size > max_size.to_i
else
image = params[:image].to_s.strip
return render_error('请上传正确的图片') if image.blank?
@image = Util.convert_base64_image(image, max_size: max_size.to_i)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
def avatar_path(organization)
ApplicationController.helpers.disk_filename(organization.class, organization.id)
end
def organization_params
params.permit(:name, :description, :website, :location,
:repo_admin_change_team_access, :visibility,
:max_repo_creation)
end
def password
params.fetch(:password, "")
end
def load_organization
@organization = Organization.find_by(login: params[:id]) || Organization.find_by(id: params[:id])
return render_not_found("组织不存在") if @organization.nil?
return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition
end
def sort_by
params.fetch(:sort_by, "created_at")
end
def sort_direction
params.fetch(:sort_direction, "desc")
end
end

View File

@ -0,0 +1,45 @@
class Organizations::ProjectsController < Organizations::BaseController
before_action :load_organization
def index
public_projects_sql = @organization.projects.where(is_public: true).to_sql
private_projects_sql = @organization.projects
.where(is_public: false)
.joins(team_projects: {team: :team_users})
.where(team_users: {user_id: current_user.id}).to_sql
@projects = Project.from("( #{ public_projects_sql} UNION #{ private_projects_sql } ) AS projects")
@projects = @projects.ransack(name_or_identifier_cont: params[:search]).result if params[:search].present?
@projects = @projects.includes(:owner).order("projects.#{sort} #{sort_direction}")
@projects = paginate(@projects)
end
def search
tip_exception("请输入搜索关键词") if params[:search].nil?
public_projects_sql = @organization.projects.where(is_public: true).to_sql
private_projects_sql = @organization.projects
.where(is_public: false)
.joins(team_projects: {team: :team_users})
.where(team_users: {user_id: current_user.id}).to_sql
@projects = Project.from("( #{ public_projects_sql} UNION #{ private_projects_sql } ) AS projects")
@projects = @projects.ransack(name_or_identifier_cont: params[:search]).result
@projects = @projects.includes(:owner).order("projects.#{sort} #{sort_direction}")
end
private
def load_organization
@organization = Organization.find_by(login: params[:organization_id]) || Organization.find_by(id: params[:organization_id])
return render_not_found("组织不存在") if @organization.nil?
return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition
end
def sort
params.fetch(:sort_by, "updated_on")
end
def sort_direction
params.fetch(:sort_direction, "desc")
end
end

View File

@ -0,0 +1,58 @@
class Organizations::TeamProjectsController < Organizations::BaseController
before_action :load_organization
before_action :load_team
before_action :load_operate_project, :check_user_can_edit_org, only: [:create, :destroy]
before_action :load_team_project, only: [:destroy]
def index
@team_projects = @team.team_projects
@team_projects = paginate(@team_projects)
end
def create
tip_exception("该组织团队项目包括组织所有项目,不允许更改") if @team.includes_all_project
ActiveRecord::Base.transaction do
@team_project = TeamProject.build(@organization.id, @team.id, @operate_project.id)
Gitea::Organization::TeamProject::CreateService.call(@organization.gitea_token, @team.gtid, @organization.login, @operate_project.identifier)
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def destroy
tip_exception("该组织团队项目包括组织所有项目,不允许更改") if @team.includes_all_project
ActiveRecord::Base.transaction do
@team_project.destroy!
Gitea::Organization::TeamProject::DeleteService.call(@organization.gitea_token, @team.gtid, @organization.login, @operate_project.identifier)
render_ok
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
private
def load_organization
@organization = Organization.find_by(login: params[:organization_id]) || Organization.find_by(id: params[:organization_id])
return render_not_found("组织不存在") if @organization.nil?
return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition
end
def load_team
@team = Team.find_by_id(params[:team_id])
return render_not_found("组织团队不存在") if @team.nil?
return render_forbidden("没有查看组织团队的权限") if team_not_found_condition
end
def load_operate_project
@operate_project = Project.find_by(id: project_mark) || Project.find_by(identifier: project_mark)
tip_exception("项目不存在") if @operate_project.nil?
end
def load_team_project
@team_project = TeamProject.find_by(organization_id: @organization.id, team_id: @team.id, project_id: @operate_project.id)
tip_exception("组织团队项目不存在") if @team_project.nil?
end
end

View File

@ -0,0 +1,76 @@
class Organizations::TeamUsersController < Organizations::BaseController
before_action :load_organization, :load_team
before_action :load_operate_user, only: [:create, :destroy]
before_action :load_team_user, only: [:destroy]
before_action :check_user_can_edit_org, only: [:create, :destroy]
def index
@team_users = @team.team_users.includes(:user)
search = params[:search].to_s.downcase
@team_users = @team_users.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present?
@team_users = kaminari_paginate(@team_users)
end
def create
ActiveRecord::Base.transaction do
@team_user = TeamUser.build(@organization.id, @operate_user.id, @team.id)
@organization_user = OrganizationUser.build(@organization.id, @operate_user.id)
Gitea::Organization::TeamUser::CreateService.call(@organization.gitea_token, @team.gtid, @operate_user.login)
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def destroy
tip_exception("您不能从所有者团队中删除最后一个用户") if @team.owner? && @organization.is_owner_team_last_one?(@operate_user.id)
ActiveRecord::Base.transaction do
@team_user.destroy!
Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, @operate_user.login)
render_ok
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def quit
@team_user = @team.team_users.find_by(user_id: current_user.id)
tip_exception("您不在该组织团队中") if @team_user.nil?
tip_exception("您不能从所有者团队中删除最后一个用户") if @team.owner? && @organization.is_owner_team_last_one?(current_user.id)
ActiveRecord::Base.transaction do
@team_user.destroy!
Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, current_user.login)
render_ok
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
private
def load_organization
@organization = Organization.find_by(login: params[:organization_id]) || Organization.find_by(id: params[:organization_id])
return render_not_found("组织不存在") if @organization.nil?
return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition
end
def load_team
@team = Team.find_by_id(params[:team_id])
return render_not_found("组织团队不存在") if @team.nil?
return render_forbidden("没有查看组织团队的权限") if team_not_found_condition
end
def load_operate_user
@operate_user = User.find_by(login: user_mark) if user_mark.present?
tip_exception("平台用户不存在") if @operate_user.nil?
end
def load_team_user
@team_user = TeamUser.find_by(team_id: @team.id, user_id: @operate_user.id)
tip_exception("组织团队成员不存在") if @team_user.nil?
end
end

View File

@ -0,0 +1,77 @@
class Organizations::TeamsController < Organizations::BaseController
before_action :load_organization
before_action :load_team, only: [:show, :update, :destroy]
before_action :check_user_can_edit_org, only: [:create, :update, :destroy]
def index
#if @organization.is_owner?(current_user) || current_user.admin?
@teams = @organization.teams
#else
# @teams = @organization.teams.joins(:team_users).where(team_users: {user_id: current_user.id})
#end
@is_admin = can_edit_org?
@teams = @teams.includes(:team_units, :team_users)
@teams = kaminari_paginate(@teams)
end
def search
tip_exception("请输入搜索关键词") if params[:search].nil?
if @organization.is_owner?(current_user) || current_user.admin?
@teams = @organization.teams
else
@teams = @organization.teams.joins(:team_users).where(team_users: {user_id: current_user.id})
end
@is_admin = can_edit_org?
@teams = @teams.ransack(name_cont: params[:search]).result if params[:search].present?
@teams = @teams.includes(:team_units, :team_users)
end
def show
@is_admin = can_edit_org?
@is_member = @team.is_member?(current_user.id)
end
def create
@team = Organizations::Teams::CreateService.call(current_user, @organization, team_params)
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def update
@team = Organizations::Teams::UpdateService.call(current_user, @team, team_params)
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def destroy
tip_exception("组织团队不允许被删除") if @team.owner?
ActiveRecord::Base.transaction do
Gitea::Organization::Team::DeleteService.call(@organization.gitea_token, @team.gtid)
@team.destroy!
end
render_ok
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
private
def team_params
params.permit(:name, :description, :authorize, :includes_all_project, :can_create_org_project, :unit_types => [])
end
def load_organization
@organization = Organization.find_by(login: params[:organization_id]) || Organization.find_by(id: params[:organization_id])
return render_not_found("组织不存在") if @organization.nil?
return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition
end
def load_team
@team = Team.find_by_id(params[:id])
return render_not_found("组织团队不存在") if @team.nil?
return render_forbidden("没有查看组织团队的权限") if team_not_found_condition
end
end

View File

@ -0,0 +1,12 @@
class OwnersController < ApplicationController
before_action :require_login
def index
@owners = []
@owners += [current_user]
@owners += Organization.joins(team_users: :team)
.where(team_users: {user_id: current_user.id},
teams: {can_create_org_project: true})
.distinct
end
end

View File

@ -0,0 +1,24 @@
class Projects::ProjectUnitsController < Projects::BaseController
def index
@project_units = @project.project_units
end
def create
if current_user.admin? || @project.manager?(current_user)
ActiveRecord::Base.transaction do
ProjectUnit.update_by_unit_types!(@project, unit_types)
render_ok
end
else
render_forbidden('你没有权限操作')
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
private
def unit_types
params.fetch(:unit_types, [])
end
end

View File

@ -0,0 +1,47 @@
class Projects::TeamsController < Projects::BaseController
before_action :load_operate_team, only: [:create, :destroy]
before_action :load_team_project, only: :destroy
def index
if @project.owner.is_a?(Organization)
@teams = Team.joins(:team_projects).where(team_projects: {project_id: @project.id})
else
@teams = Team.none
end
@teams = paginate(@teams)
end
def create
ActiveRecord::Base.transaction do
@team_project = TeamProject.build(@owner.id, @operate_team.id, @project.id)
Gitea::Organization::TeamProject::CreateService.call(@owner.gitea_token, @operate_team.gtid, @owner.login, @project.identifier)
render_ok
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def destroy
ActiveRecord::Base.transaction do
@team_project.destroy!
Gitea::Organization::TeamProject::DeleteService.call(@owner.gitea_token, @operate_team.gtid, @owner.login, @project.identifier)
render_ok
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
private
def load_operate_team
@operate_team = Team.find_by(id: params[:team_id]) || Team.find_by(id: params[:id])
tip_exception("项目不存在") if @operate_team.nil?
tip_exception("该组织团队拥有组织所有项目,无法进行操作") if @operate_team.includes_all_project
end
def load_team_project
@team_project = TeamProject.find_by(organization_id: @owner.id, team_id: @operate_team.id, project_id: @project.id)
tip_exception("组织团队项目不存在") if @team_project.nil?
end
end

View File

@ -2,16 +2,31 @@ class ProjectsController < ApplicationController
include ApplicationHelper
include OperateProjectAbilityAble
include ProjectsHelper
before_action :require_login, except: %i[index branches group_type_list simple show fork_users praise_users watch_users recommend about]
before_action :require_login, except: %i[index branches group_type_list simple show fork_users praise_users watch_users recommend about menu_list]
before_action :load_project, except: %i[index group_type_list migrate create recommend]
before_action :authorizate_user_can_edit_project!, only: %i[update]
before_action :project_public?, only: %i[fork_users praise_users watch_users]
def menu_list
menu = []
menu.append(menu_hash_by_name("home"))
menu.append(menu_hash_by_name("code")) if @project.has_menu_permission("code")
menu.append(menu_hash_by_name("issues")) if @project.has_menu_permission("issues")
menu.append(menu_hash_by_name("pulls")) if @project.has_menu_permission("pulls")
menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops")
menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions")
menu.append(menu_hash_by_name("activity"))
menu.append(menu_hash_by_name("setting")) if current_user.admin? || @project.manager?(current_user)
render json: menu
end
def index
scope = Projects::ListQuery.call(params)
# @projects = kaminari_paginate(scope)
@projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, owner: :user_extension)
@projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)
category_id = params[:category_id]
@total_count =
@ -44,7 +59,10 @@ class ProjectsController < ApplicationController
end
def branches
@branches = @project.forge? ? Gitea::Repository::Branches::ListService.new(@owner, @project.identifier).call : []
return @branches = [] unless @project.forge?
result = Gitea::Repository::Branches::ListService.call(@owner, @project.identifier)
@branches = result.is_a?(Hash) && result.key?(:status) ? [] : result
end
def group_type_list
@ -70,17 +88,19 @@ class ProjectsController < ApplicationController
def update
ActiveRecord::Base.transaction do
# Projects::CreateForm.new(project_params).validate!
private = params[:private]
private = params[:private] || false
new_project_params = project_params.except(:private).merge(is_public: !private)
@project.update_attributes!(new_project_params)
gitea_params = {
private: private,
default_branch: params[:default_branch]
default_branch: @project.default_branch,
website: @project.website
}
if [true, false].include? private
new_project_params = project_params.merge(is_public: !private)
Gitea::Repository::UpdateService.call(@owner, @project.identifier, gitea_params)
@project.repository.update_column(:hidden, private)
end
@project.update_attributes!(new_project_params)
end
rescue Exception => e
uid_logger_error(e.message)
@ -91,7 +111,7 @@ class ProjectsController < ApplicationController
end
def destroy
if current_user.admin? || @project.owner?(current_user)
if current_user.admin? || @project.manager?(current_user)
ActiveRecord::Base.transaction do
Gitea::Repository::DeleteService.new(@project.owner, @project.identifier).call
@project.destroy!
@ -128,7 +148,7 @@ class ProjectsController < ApplicationController
end
def recommend
@projects = Project.recommend.includes(:repository, :project_category, owner: :user_extension).limit(5)
@projects = Project.recommend.includes(:repository, :project_category, :owner).limit(5)
end
def about
@ -162,8 +182,8 @@ class ProjectsController < ApplicationController
private
def project_params
params.permit(:user_id, :name, :description, :repository_name,
:project_category_id, :project_language_id, :license_id, :ignore_id)
params.permit(:user_id, :name, :description, :repository_name, :website,
:project_category_id, :project_language_id, :license_id, :ignore_id, :private)
end
def mirror_params

View File

@ -8,6 +8,7 @@ class PullRequestsController < ApplicationController
def index
return render_not_found unless @project.has_menu_permission("pulls")
# @issues = Gitea::PullRequest::ListService.new(@user,@repository.try(:identifier)).call #通过gitea获取
issues = @project.issues.issue_pull_request.issue_index_includes.includes(pull_request: :user)
issues = issues.where(is_private: false) unless current_user.present? && (current_user.admin? || @project.member?(current_user))
@ -228,7 +229,8 @@ class PullRequestsController < ApplicationController
}
@requests_params = @local_params.merge({
assignee: current_user.try(:login),
assignees: ["#{params[:assigned_login].to_s}"],
# assignees: ["#{params[:assigned_login].to_s}"],
assignees: ["#{current_user.try(:login).to_s}"],
labels: params[:issue_tag_ids],
due_date: Time.now
})

View File

@ -7,10 +7,29 @@ class RepositoriesController < ApplicationController
before_action :load_repository
before_action :authorizate!, except: [:sync_mirror, :tags, :commit]
before_action :authorizate_user_can_edit_repo!, only: %i[sync_mirror]
before_action :get_ref, only: %i[entries sub_entries top_counts]
before_action :get_ref, only: %i[entries sub_entries top_counts file]
before_action :get_latest_commit, only: %i[entries sub_entries top_counts]
before_action :get_statistics, only: %i[top_counts]
def files
result = @project.educoder? ? nil : Gitea::Repository::Files::GetService.call(@owner, @project.identifier, @ref, params[:search], @owner.gitea_token)
render json: result
end
# 新版项目详情
def detail
@user = current_user
@result = Repositories::DetailService.call(@owner, @repository, @user)
@project_fork_id = @project.try(:forked_from_project_id)
if @project_fork_id.present?
@fork_project = Project.find_by(id: @project_fork_id)
@fork_project_user = @fork_project.owner
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def show
@user = current_user
@repo = @project.repository
@ -84,10 +103,17 @@ class RepositoriesController < ApplicationController
end
def tags
@tags = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})
result = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})
@tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
end
def contributors
@contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier)
end
def edit
return render_forbidden if !@project.manager?(current_user) && !current_user.admin?
end
def create_file
@ -186,7 +212,7 @@ class RepositoriesController < ApplicationController
end
def get_ref
@ref = params[:ref] || "master"
@ref = params[:ref] || @project&.default_branch
end
def get_latest_commit

View File

@ -1,6 +1,58 @@
class SettingsController < ApplicationController
def show
@old_projects_url = nil
@old_projects_url = "https://www.trustie.net/users/#{current_user.try(:login)}/projects" if User.current.logged?
@old_projects_url = nil
get_add_menu
get_common_menu
get_personal_menu
end
private
def get_add_menu
@add = []
Site.add.select(:id, :name, :url, :key).to_a.map(&:serializable_hash).each do |site|
hash = {}
site.each {|k, v|
hash.merge!("#{k}": get_site_url(k, v))
}
@add << hash
end
end
def get_common_menu
@common = {}
Site.common.select(:url, :key).each do |site|
next if site["url"].to_s.include?("current_user") && !User.current.logged?
@common.merge!("#{site["key"]}": append_http(reset_site_url(site["url"])))
end
end
def get_personal_menu
@personal = []
if User.current.logged?
Site.personal.select(:id, :name, :url, :key).to_a.map(&:serializable_hash).each do |site|
hash = {}
site.each {|k, v|
hash.merge!("#{k}": get_site_url(k, v))
}
@personal << hash
end
end
end
def get_site_url(key, value)
url = reset_site_url(value)
key.to_s === "url" && !value.to_s.start_with?("http") ? [request.protocol, request.host_with_port, value].join('') : url
end
def reset_site_url(url)
return url unless url.to_s.include?("current_user")
split_arr = url.split('current_user')
split_arr.length > 1 ? split_arr.join(current_user&.login) : (split_arr << current_user&.login).join('')
end
def append_http(url)
url.to_s.start_with?("http") ? url : [request.protocol, request.host_with_port, url].join('')
end
end

View File

@ -0,0 +1,25 @@
class Users::OrganizationsController < Users::BaseController
def index
if current_user.logged?
logged_organizations_sql = observed_user.organizations.with_visibility(%w(common limited)).to_sql
privacy_organizations_sql = observed_user.organizations.with_visibility("privacy").joins(:organization_users).where(organization_users: {user_id: current_user.id}).to_sql
@organizations = Organization.from("( #{ logged_organizations_sql } UNION #{ privacy_organizations_sql } ) AS users")
else
@organizations = observed_user.organizations.with_visibility("common")
end
@organizations = @organizations.ransack(login_cont: params[:search]).result if params[:search].present?
@organizations = @organizations.includes(:organization_extension).order("organization_extensions.#{sort_by} #{sort_direction}")
@organizations = kaminari_paginate(@organizations)
end
private
def sort_by
params.fetch(:sort_by, "created_at")
end
def sort_direction
params.fetch(:sort_direction, "desc")
end
end

View File

@ -31,6 +31,7 @@ class UsersController < ApplicationController
#用户的组织数量
# @user_composes_count = @user.composes.size
@user_composes_count = 0
@user_org_count = User.current.logged? ? @user.organizations.with_visibility(%w(common limited)).size + @user.organizations.with_visibility("privacy").joins(:organization_users).where(organization_users: {user_id: current_user.id}).size : @user.organizations.with_visibility("common").size
user_projects = User.current.logged? && (User.current.admin? || User.current.login == @user.login) ? @user.projects : @user.projects.visible
@projects_common_count = user_projects.common.size
@projects_mirrior_count = user_projects.mirror.size

View File

@ -124,7 +124,7 @@ class VersionReleasesController < ApplicationController
private
def set_user
@user = @repository.user
@user = @repository.owner
end
def find_version

View File

@ -5,6 +5,7 @@ class VersionsController < ApplicationController
before_action :set_version, only: [:edit, :update, :destroy, :show,:update_status]
def index
return render_not_found unless @project.has_menu_permission("versions")
@user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user))
order_name = params[:order_name] || "created_on"
order_type = params[:order_type] || "desc"

View File

@ -168,6 +168,55 @@ Remember — a happy kitten is an authenticated kitten!
</aside>
## 项目导航
获取项目导航信息
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/yystopf/ceshi/menu_list | jq
```
```javascript
await octokit.request('GET /api/yystopf/ceshi/menu_list')
```
### HTTP 请求
`GET api/:owner/:repo/menu_list`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner |是| |string |用户登录名
repo |是| |string |项目标识identifier
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
menu_name |string|导航名称, home:主页,code:代码库,issues:易修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置
> 返回的JSON示例:
```json
[
{
"menu_name": "home"
},
{
"menu_name": "code"
},
{
"menu_name": "pulls"
},
{
"menu_name": "activity"
}
]
```
## 项目主页
获取项目主页信息
@ -212,6 +261,84 @@ attachments |array|附件
Remember — a happy kitten is an authenticated kitten!
</aside>
## 项目模块信息
项目模块信息
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/yystopf/ceshi/project_units.json
```
```javascript
await octokit.request('GET /api/yystopf/ceshi/project_units')
```
### HTTP 请求
`GET /api/yystopf/ceshi/project_units`
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|type |string|模块名称 |
> 返回的JSON示例:
```json
[
{
"type": "code"
},
{
"type": "pulls"
},
{
"type": "issues"
}
]
```
## 更改项目模块展示
更改项目模块展示
> 示例:
```shell
curl -X POST \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d "{ \"unit_typs\": [\"code\", \"pulls\"]}" \
http://localhost:3000/api/yystopf/ceshi/project_units.json
```
```javascript
await octokit.request('POST /api/yystopf/ceshi/project_units')
```
### HTTP 请求
`POST /api/yystopf/ceshi/project_units`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|unit_types |是| |array | 项目模块内容, 支持以下参数:code:代码库,issues:易修,pulls:合并请求,devops:工作流,versions:里程碑 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|status |int|返回状态, 0: 表示操作成功 |
|message |string|返回信息说明|
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 创建项目
创建项目

View File

@ -97,6 +97,182 @@ await octokit.request('GET /api/jasder/jasder_test/simple.json')
}
```
## 仓库详情(新版)
仓库详情
> 示例:
```shell
curl -X GET http://localhost:3000/api/yystopf/ceshi/detail.json
```
```javascript
await octokit.request('GET /api/yystopf/ceshi/detail.json')
```
### HTTP 请求
`GET /api/:owner/:repo/detail`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|content |string |仓库简介 |
|website |string |仓库网址 |
|readme |string |readme文件
|identifier |string |项目标识 |
|name |string |项目名称 |
|issues_count |int |项目issue数量|
|pull_requests_count |int |项目合并请求数量|
|project_identifier |int |项目标识|
|praises_count |int |项目点赞数量|
|forked_count |int |项目复刻数量|
|watchers_count |int |项目关注数量|
|versions_count |int |项目里程碑数量|
|version_releases_count |int |项目发行版数量|
|version_releasesed_count |int |项目发行版已发行数量|
|permission |string |项目权限|
|mirror_url |string |镜像地址|
|mirror |bool |是否为镜像项目|
|type |int |项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目|
|open_devops |int |是否开启devops|
|watched |bool |是否关注|
|praised |bool |是否点赞|
|status |int |项目状态|
|forked_from_project_id |int |fork项目id|
|fork_info |object |fork项目信息|
|size |string |仓库大小|
|ssh_url |string |项目ssh地址|
|clone_url |string |项目克隆地址|
|default_branch |string |仓库默认分支|
|empty |bool |仓库是否为空|
|full_name |string |仓库全称|
|private |bool |仓库是否为私有项目|
|license_name |string |许可证名称|
|release_versions.list.name |string |项目issue数量|
|release_versions.list.tag_name |string |发行版标签名称|
|release_versions.list.created_at |string |发行版创建时间|
|release_versions.total_count |int |发行版数量|
|branches.list.name |string |分支名称|
|branches.total_count |int |分支数量|
|tags.list.name |string |标签名称|
|tags.total_count |int |标签数量|
|contributors.list.contributions|int |贡献数量|
|contributors.list.login |string |贡献者登录名|
|contributors.list.name |string |贡献者用户名称|
|contributors.list.image_url |string |贡献者头像|
|languages |object |项目语言占比|
> 返回的JSON示例:
```json
{
"content": "仓库简介",
"website": "仓库网址",
"readme": {
"type": "file",
"encoding": "base64",
"size": 9,
"name": "README.md",
"path": "README.md",
"content": "# ceshi\n\n",
"sha": ""
},
"identifier": "ceshi",
"name": "测试项目",
"project_id": 2,
"repo_id": 2,
"issues_count": 0,
"pull_requests_count": 0,
"project_identifier": "ceshi",
"praises_count": 0,
"forked_count": 0,
"watchers_count": 0,
"versions_count": 0,
"version_releases_count": 0,
"version_releasesed_count": 0,
"permission": "Reporter",
"mirror_url": null,
"mirror": false,
"type": 0,
"open_devops": false,
"watched": false,
"praised": false,
"status": 1,
"forked_from_project_id": 1,
"fork_info": {
"fork_form_name": "测试项目",
"fork_project_user_login": "ceshi_org",
"fork_project_identifier": "ceshi",
"fork_project_user_name": "ceshi_org"
},
"size": "25.0 KB",
"ssh_url": "virus@localhost:yystopf/ceshi.git",
"clone_url": "http://localhost:10080/yystopf/ceshi.git",
"default_branch": "master",
"empty": false,
"full_name": "yystopf/ceshi",
"private": false,
"license_name": "gnu-javamail-exception",
"release_versions": {
"list": [
{
"id": 2,
"name": "vvvv",
"tag_name": "v1.1",
"created_at": "2019-07-18 10:16"
}
],
"total_count": 1
},
"branches": {
"list": [
{
"name": "master"
}
],
"total_count": 1
},
"tags": {
"list": [
{
"name": "v1.1"
},
{
"name": "v1.0"
}
],
"total_count": 2
},
"contributors": {
"list": [
{
"contributions": 1,
"gid": 2,
"login": "yystopf",
"type": "User",
"name": "yystopf",
"image_url": "avatars/User/b"
}
],
"total_count": 1
},
"languages": {
"HTML": "50.9%",
"Ruby": "25.6%",
"JavaScript": "21.4%",
"CSS": "1.3%",
"CoffeeScript": "0.7%",
"Shell": "0.1%"
}
}
```
## 编辑仓库信息
编辑仓库信息
@ -463,6 +639,78 @@ await octokit.request('GET /api/jasder/jasder_test/collaborators.json')
Success Data.
</aside>
## 获取仓库所有文件
获取仓库所有文件
> 示例:
```shell
curl -X GET \
-d "ref=develop" \
http://localhost:3000/api/yystopf/ceshi/files.json
```
```javascript
await octokit.request('GET /api/yystopf/ceshi/files.json')
```
### HTTP 请求
`GET /api/:owner/:repo/files`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|search |否| |string |文件搜索关键词 |
|ref |是| |string |分支名,默认为仓库默认分支 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|name |string |文件名称|
|path |string |文件路径|
|sha |string |文件标识|
|type |string |文件类型|
|size |int |文件大小|
|url |string |文件地址|
> 返回的JSON示例:
```json
[
{
"name": ".gitignore",
"path": ".gitignore",
"sha": "f83922d01ae60f6e637a1a2b9f08871b4f87dfc8",
"type": "file",
"size": 63,
"url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/.gitignore?ref=master",
"html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/.gitignore"
},
{
"name": "LICENSE",
"path": "LICENSE",
"sha": "8f3b9ab0d08afd3a624d822e3971a2f42b3bc2b9",
"type": "file",
"size": 341,
"url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/LICENSE?ref=master",
"html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/LICENSE"
},
{
"name": "README.md",
"path": "README.md",
"sha": "1bc8a60ac6ddc876ebc4b60fc68991435bfad93e",
"type": "file",
"size": 9,
"url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/README.md?ref=master",
"html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/README.md"
}
]
```
## 获取仓库代码目录
获取仓库代码目录

View File

@ -1,13 +1,13 @@
class Projects::CreateForm < BaseForm
REPOSITORY_NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
attr_accessor :user_id, :name, :description, :repository_name, :project_category_id,
:project_language_id, :ignore_id, :license_id, :private
:project_language_id, :ignore_id, :license_id, :private, :owner
validates :user_id, :name, :description,:repository_name,
:project_category_id, :project_language_id, presence: true
validates :repository_name, format: { with: REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
validate :check_ignore, :check_license
validate :check_ignore, :check_license, :check_owner, :check_max_repo_creation
validate do
check_project_category(project_category_id)
check_project_language(project_language_id)
@ -20,4 +20,15 @@ class Projects::CreateForm < BaseForm
def check_ignore
raise "ignore_id值无效." if ignore_id && Ignore.find_by(id: ignore_id).blank?
end
def check_owner
@owner = Owner.find_by(id: user_id)
raise "user_id值无效." if user_id && owner.blank?
end
def check_max_repo_creation
return unless owner.is_a?(Organization)
return if owner.max_repo_creation <= -1
raise "已超过组织设置最大仓库数" if owner.max_repo_creation == owner.num_projects
end
end

View File

@ -2,7 +2,7 @@ class Projects::MigrateForm < BaseForm
REPOSITORY_NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
URL_REGEX = /\A(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?\z/i
attr_accessor :user_id, :name, :description, :repository_name, :project_category_id, :project_language_id, :clone_addr, :private, :is_mirror, :auth_username, :auth_password
attr_accessor :user_id, :name, :description, :repository_name, :project_category_id, :project_language_id, :clone_addr, :private, :is_mirror, :auth_username, :auth_password, :owner
validates :user_id, :name, :description,:repository_name, :project_category_id, :project_language_id, presence: true
validates :repository_name, format: { with: REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
@ -12,6 +12,18 @@ class Projects::MigrateForm < BaseForm
check_repository_name(user_id, repository_name) unless repository_name.blank?
check_project_category(project_category_id)
check_project_language(project_language_id)
check_owner
check_max_repo_creation
end
def check_owner
@owner = Owner.find_by(id: user_id)
raise "user_id值无效." if user_id && owner.blank?
end
def check_max_repo_creation
return unless owner.is_a?(Organization)
return if owner.max_repo_creation <= -1
raise "已超过组织设置最大仓库数" if owner.max_repo_creation == owner.num_projects
end
end

View File

@ -143,7 +143,7 @@ module ApplicationHelper
def url_to_avatar(source)
if File.exist?(disk_filename(source&.class, source&.id))
ctime = File.ctime(disk_filename(source.class, source.id)).to_i
if source.class.to_s == 'User'
if %w(User Organization).include?(source.class.to_s)
File.join(relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
else
File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"

View File

@ -1,4 +1,9 @@
module ProjectsHelper
def menu_hash_by_name(name)
{
menu_name: name
}
end
def render_zh_project_type(project_type)
case project_type
@ -61,12 +66,14 @@ module ProjectsHelper
{
login: project.project_educoder.owner,
name: project.project_educoder.owner,
type: 'Educoder',
image_url: project.project_educoder.image_url
}
else
{
login: @owner.login,
name: @owner.real_name,
type: @owner.type,
image_url: url_to_avatar(@owner)
}
end

View File

@ -28,39 +28,43 @@ module RepositoriesHelper
def render_commit_author(author_json)
return nil if author_json.blank?
find_user_by_login author_json['login']
find_user_by_login author_json['name']
end
def readme_render_decode64_content(str, path)
return nil if str.blank?
content = Base64.decode64(str).force_encoding('UTF-8')
begin
content = Base64.decode64(str).force_encoding('UTF-8')
c_regex = /\!\[.*?\]\((.*?)\)/
src_regex = /src=\"(.*?)\"/
ss = content.to_s.scan(c_regex)
ss_src = content.to_s.scan(src_regex)
total_images = ss + ss_src
if total_images.length > 0
total_images.each do |s|
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
c_regex = /\!\[.*?\]\((.*?)\)/
src_regex = /src=\"(.*?)\"/
ss = content.to_s.scan(c_regex)
ss_src = content.to_s.scan(src_regex)
total_images = ss + ss_src
if total_images.length > 0
total_images.each do |s|
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
if r_content.include?("?")
new_r_content = r_content + "&raw=true"
else
new_r_content = r_content + "?raw=true"
end
unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:")
new_r_content = "#{path}" + new_r_content
end
content = content.gsub(/#{r_content}/, new_r_content)
end
if r_content.include?("?")
new_r_content = r_content + "&raw=true"
else
new_r_content = r_content + "?raw=true"
end
unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:")
new_r_content = "#{path}" + new_r_content
end
content = content.gsub(/#{r_content}/, new_r_content)
end
return content
rescue
return str
end
return content
end
# unix_time values for example: 1604382982

View File

@ -5,7 +5,7 @@ class SyncMirroredRepositoryJob < ApplicationJob
repo = Repository.find_by(id: repo_id)
current_user = User.find_by(id: user_id)
return if repo.blank? || current_user.blank?
result = Gitea::Repository::SyncMirroredService.new(repo.user.login, repo.identifier, token: current_user.gitea_token).call
result = Gitea::Repository::SyncMirroredService.new(repo.owner.login, repo.identifier, token: current_user.gitea_token).call
repo&.mirror.set_status! if result[:status] === 200
end
end

View File

@ -85,4 +85,24 @@ class Ci::Drone::API < Ci::Drone::Request
post(endpoint, "/api/users", {login: options[:login], email: options[:email], avatar_url:options[:avatar_url],active:true, drone_token: options[:token]})
end
# Creates a secret.
def create_secret
post(endpoint, "/api/repos/#{owner}/#{repo}/secrets", {name: options[:name], data: options[:data], pull_request:true, drone_token: drone_token})
end
# Update a secret.
def update_secret
patch(endpoint, "/api/repos/#{owner}/#{repo}/secrets/#{options[:name]}", { data: options[:data], pull_request:true, drone_token: drone_token})
end
# list of secrets.
def secrets
get(endpoint, "/api/repos/#{owner}/#{repo}/secrets", drone_token: drone_token)
end
# delete secret.
def delete_secret
delete(endpoint, "/api/repos/#{owner}/#{repo}/secrets/#{options[:name]}", drone_token: drone_token)
end
end

View File

@ -8,11 +8,12 @@ module ProjectOperable
has_many :developers, -> { joins(:roles).where(roles: { name: 'Developer' }) }, class_name: 'Member'
has_many :reporters, -> { joins(:roles).where(roles: { name: 'Reporter' }) }, class_name: 'Member'
has_many :writable_members, -> { joins(:roles).where.not(roles: {name: 'Reporter'}) }, class_name: 'Member'
has_many :team_projects, dependent: :destroy
end
def add_member!(user_id, role_name='Developer')
member = members.create!(user_id: user_id)
set_developer_role(member)
set_developer_role(member, role_name)
end
def remove_member!(user_id)
@ -21,7 +22,13 @@ module ProjectOperable
end
def member?(user_id)
members.exists?(user_id: user_id)
if owner.is_a?(User)
members.exists?(user_id: user_id)
elsif owner.is_a?(Organization)
members.exists?(user_id: user_id) || team_projects.joins(team: :team_users).where(team_users: {user_id: user_id}).present?
else
false
end
end
# 除了项目创建者本身
@ -35,27 +42,54 @@ module ProjectOperable
end
def owner?(user)
self.owner == user
if owner.is_a?(User)
self.owner == user
elsif owner.is_a?(Organization)
owner.is_owner?(user.id)
else
false
end
end
# 项目管理员(包含项目拥有者),权限:仓库设置、仓库可读可写
def manager?(user)
managers.exists?(user_id: user.id)
if owner.is_a?(User)
managers.exists?(user_id: user.id)
elsif owner.is_a?(Organization)
managers.exists?(user_id: user.id) || owner.is_admin?(user.id)
else
false
end
end
# 项目开发者,可读可写权限
def develper?(user)
developers.exists?(user_id: user.id)
if owner.is_a?(User)
developers.exists?(user_id: user.id)
elsif owner.is_a?(Organization)
developers.exists?(user_id: user.id) || owner.is_write?(user.id)
else
false
end
end
# 报告者,只有可读权限
def reporter?(user)
reporters.exists?(user_id: user.id)
if owner.is_a?(User)
reporters.exists?(user_id: user.id)
elsif owner.is_a?(Organization)
reporters.exists?(user_id: user.id) || owner.is_read?(user.id)
else
false
end
end
def set_developer_role(member)
role = Role.find_by_name 'Developer'
def set_developer_role(member, role_name)
role = Role.find_by(name: role_name)
member.member_roles.create!(role: role)
end
def has_menu_permission(unit_type)
self.project_units.where(unit_type: unit_type).exists?
end
end

127
app/models/organization.rb Normal file
View File

@ -0,0 +1,127 @@
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# login :string(255) default(""), not null
# hashed_password :string(40) default(""), not null
# firstname :string(30) default(""), not null
# lastname :string(255) default(""), not null
# mail :string(60)
# admin :boolean default("0"), not null
# status :integer default("1"), not null
# last_login_on :datetime
# language :string(5) default("")
# auth_source_id :integer
# created_on :datetime
# updated_on :datetime
# type :string(255)
# identity_url :string(255)
# mail_notification :string(255) default(""), not null
# salt :string(64)
# gid :integer
# visits :integer default("0")
# excellent_teacher :integer default("0")
# excellent_student :integer default("0")
# phone :string(255)
# authentication :boolean default("0")
# grade :integer default("0")
# experience :integer default("0")
# nickname :string(255)
# show_realname :boolean default("1")
# professional_certification :boolean default("0")
# ID_number :string(255)
# certification :integer default("0")
# homepage_teacher :boolean default("0")
# homepage_engineer :boolean default("0")
# is_test :integer default("0")
# ecoder_user_id :integer default("0")
# business :boolean default("0")
# profile_completed :boolean default("0")
# laboratory_id :integer
# platform :string(255) default("0")
# gitea_token :string(255)
# gitea_uid :integer
# is_shixun_marker :boolean default("0")
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
#
# Indexes
#
# index_users_on_ecoder_user_id (ecoder_user_id)
# index_users_on_homepage_engineer (homepage_engineer)
# index_users_on_homepage_teacher (homepage_teacher)
# index_users_on_laboratory_id (laboratory_id)
# index_users_on_login (login)
# index_users_on_mail (mail)
# index_users_on_type (type)
#
class Organization < Owner
alias_attribute :name, :login
NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
default_scope { where(type: "Organization") }
has_one :organization_extension, dependent: :destroy
has_many :teams, dependent: :destroy
has_many :organization_users, dependent: :destroy
has_many :team_users, dependent: :destroy
validates :login, presence: true
validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, case_sensitive: false
validates :login, format: { with: NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
delegate :description, :website, :location, :repo_admin_change_team_access,
:visibility, :max_repo_creation, :num_projects, :num_users, :num_teams, to: :organization_extension, allow_nil: true
scope :with_visibility, ->(visibility) { joins(:organization_extension).where(organization_extensions: {visibility: visibility}) if visibility.present? }
def self.build(name, gitea_token=nil)
self.create!(login: name, gitea_token: gitea_token)
end
def can_create_project?(user_id)
team_users.joins(:team).where(user_id: user_id, teams: {can_create_org_project: true}).present?
end
def is_member?(user_id)
organization_users.where(user_id: user_id).present?
end
def is_owner?(user_id)
team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(owner)}).present?
end
def is_admin?(user_id)
team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(admin owner)}).present?
end
def is_write?(user_id)
team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(write admin owner)}).present?
end
def is_read?(user_id)
team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(read write admin owner)}).present?
end
# 是不是所有者团队的最后一个成员
def is_owner_team_last_one?(user_id)
owner_team_users = team_users.joins(:team).where(teams: {authorize: %w(owner)})
owner_team_users.pluck(:user_id).include?(user_id) && owner_team_users.size == 1
end
def real_name
login
end
def show_real_name
name = lastname + firstname
if name.blank?
nickname.blank? ? login : nickname
else
name
end
end
end

View File

@ -0,0 +1,42 @@
# == Schema Information
#
# Table name: organization_extensions
#
# id :integer not null, primary key
# organization_id :integer
# description :string(255)
# website :string(255)
# location :string(255)
# repo_admin_change_team_access :boolean default("0")
# visibility :integer default("0")
# max_repo_creation :integer default("-1")
# created_at :datetime not null
# updated_at :datetime not null
# num_projects :integer default("0")
# num_users :integer default("0")
# num_teams :integer default("0")
#
# Indexes
#
# index_organization_extensions_on_organization_id (organization_id)
#
class OrganizationExtension < ApplicationRecord
belongs_to :organization
has_many :organization_users, foreign_key: :organization_id, primary_key: :organization_id
has_many :projects, foreign_key: :user_id, primary_key: :organization_id
has_many :teams, foreign_key: :organization_id, primary_key: :organization_id
enum visibility: {common: 0, limited: 1, privacy: 2}
def self.build(organization_id, description, website, location, repo_admin_change_team_access, visibility, max_repo_creation)
self.create!(organization_id: organization_id,
description: description,
website: website,
location: location,
repo_admin_change_team_access: repo_admin_change_team_access,
visibility: visibility,
max_repo_creation: max_repo_creation)
end
end

View File

@ -0,0 +1,34 @@
# == Schema Information
#
# Table name: organization_users
#
# id :integer not null, primary key
# user_id :integer
# organization_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_organization_users_on_organization_id (organization_id)
# index_organization_users_on_user_id (user_id)
#
class OrganizationUser < ApplicationRecord
belongs_to :organization
belongs_to :organization_extension, foreign_key: :organization_id, primary_key: :organization_id, counter_cache: :num_users
belongs_to :user
validates :user_id, uniqueness: {scope: :organization_id}
def self.build(organization_id, user_id)
org_user = self.find_by(organization_id: organization_id, user_id: user_id)
return org_user unless org_user.nil?
self.create!(organization_id: organization_id, user_id: user_id)
end
def teams
organization.teams.joins(:team_users).where(team_users: {user_id: user_id})
end
end

69
app/models/owner.rb Normal file
View File

@ -0,0 +1,69 @@
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# login :string(255) default(""), not null
# hashed_password :string(40) default(""), not null
# firstname :string(30) default(""), not null
# lastname :string(255) default(""), not null
# mail :string(60)
# admin :boolean default("0"), not null
# status :integer default("1"), not null
# last_login_on :datetime
# language :string(5) default("")
# auth_source_id :integer
# created_on :datetime
# updated_on :datetime
# type :string(255)
# identity_url :string(255)
# mail_notification :string(255) default(""), not null
# salt :string(64)
# gid :integer
# visits :integer default("0")
# excellent_teacher :integer default("0")
# excellent_student :integer default("0")
# phone :string(255)
# authentication :boolean default("0")
# grade :integer default("0")
# experience :integer default("0")
# nickname :string(255)
# show_realname :boolean default("1")
# professional_certification :boolean default("0")
# ID_number :string(255)
# certification :integer default("0")
# homepage_teacher :boolean default("0")
# homepage_engineer :boolean default("0")
# is_test :integer default("0")
# ecoder_user_id :integer default("0")
# business :boolean default("0")
# profile_completed :boolean default("0")
# laboratory_id :integer
# platform :string(255) default("0")
# gitea_token :string(255)
# gitea_uid :integer
# is_shixun_marker :boolean default("0")
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
#
# Indexes
#
# index_users_on_ecoder_user_id (ecoder_user_id)
# index_users_on_homepage_engineer (homepage_engineer)
# index_users_on_homepage_teacher (homepage_teacher)
# index_users_on_laboratory_id (laboratory_id)
# index_users_on_login (login)
# index_users_on_mail (mail)
# index_users_on_type (type)
#
class Owner < ApplicationRecord
self.abstract_class = true
self.table_name = "users"
include ProjectAbility
has_many :projects, foreign_key: :user_id, dependent: :destroy
has_many :repositories, foreign_key: :user_id, dependent: :destroy
end

View File

@ -52,6 +52,8 @@
# open_devops_count :integer default("0")
# recommend :boolean default("0")
# platform :integer default("0")
# default_branch :string(255) default("master")
# website :string(255)
#
# Indexes
#
@ -68,6 +70,10 @@
# index_projects_on_updated_on (updated_on)
#
class Project < ApplicationRecord
include Matchable
include Publicable
@ -85,7 +91,8 @@ class Project < ApplicationRecord
belongs_to :ignore, optional: true
belongs_to :license, optional: true
belongs_to :owner, class_name: 'User', foreign_key: :user_id, optional: true
belongs_to :owner, class_name: 'Owner', foreign_key: :user_id, optional: true
belongs_to :organization_extension, foreign_key: :user_id, primary_key: :organization_id, optional: true, counter_cache: :num_projects
belongs_to :project_category, optional: true , :counter_cache => true
belongs_to :project_language, optional: true , :counter_cache => true
has_many :project_trends, dependent: :destroy
@ -106,12 +113,16 @@ class Project < ApplicationRecord
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
has_one :project_detail, dependent: :destroy
has_many :team_projects, dependent: :destroy
has_many :project_units, dependent: :destroy
after_save :check_project_members
scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)}
scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)}
scope :recommend, -> { visible.project_statics_select.where(recommend: true) }
delegate :content, to: :project_detail, allow_nil: true
delegate :name, to: :license, prefix: true, allow_nil: true
def self.search_project(search)
@ -248,7 +259,7 @@ class Project < ApplicationRecord
def self.find_with_namespace(namespace_path, identifier)
logger.info "########namespace_path: #{namespace_path} ########identifier: #{identifier} "
user = User.find_by_login namespace_path
user = Owner.find_by_login namespace_path
project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}")
return nil if project.blank?

View File

@ -0,0 +1,35 @@
# == Schema Information
#
# Table name: project_units
#
# id :integer not null, primary key
# project_id :integer
# unit_type :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_units_on_project_id (project_id)
#
class ProjectUnit < ApplicationRecord
belongs_to :project
enum unit_type: {code: 1, issues: 2, pulls: 3, devops: 4, versions: 5}
validates :unit_type, uniqueness: { scope: :project_id}
def self.init_types(project_id)
ProjectUnit::unit_types.each do |_, v|
self.create!(project_id: project_id, unit_type: v)
end
end
def self.update_by_unit_types!(project, types)
project.project_units.where.not(unit_type: types).each(&:destroy!)
types.each do |type|
project.project_units.find_or_create_by!(unit_type: type)
end
end
end

View File

@ -33,7 +33,7 @@
class Repository < ApplicationRecord
self.inheritance_column = nil # FIX The single-table inheritance mechanism failed
belongs_to :project, :touch => true
belongs_to :user, optional: true
belongs_to :owner, class_name: 'Owner', foreign_key: :user_id, optional: true
has_one :mirror, foreign_key: :repo_id
has_one :ci_cloud_account, class_name: 'Ci::CloudAccount', foreign_key: :repo_id
has_many :version_releases, dependent: :destroy
@ -41,6 +41,8 @@ class Repository < ApplicationRecord
validates :identifier, presence: true
delegate :default_branch, to: :project, allow_nil: true
def to_param
self.identifier.parameterize
end

72
app/models/site.rb Normal file
View File

@ -0,0 +1,72 @@
# == Schema Information
#
# Table name: sites
#
# id :integer not null, primary key
# name :string(255)
# url :string(255)
# key :string(255)
# site_type :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Site < ApplicationRecord
# add: 添加类链接
# personal: 个人名下类链接,
# common: 普通链接
enum site_type: { add: 0, personal: 1, common: 2 }
def self.set_default_menu
set_add_menu!
set_personal_menu!
set_common_menu!
end
private
def self.set_add_menu!
adds= [
{name: '新建镜像项目', key: 'add_mirror_project', url: '/projects/mirror/new'},
{name: '新建托管项目', key: 'add_common', url: '/projects/deposit/new'},
{name: '新建组织', key: 'add_r', url: '/organize/new'}]
adds.each { |ele|
Site.find_or_create_by(key: ele[:key]) do |site|
site.name = ele[:name]
site.url = ele[:url]
site.site_type = Site.site_types[:add]
end
}
end
def self.set_personal_menu!
personals = [
{name: '个人中心', key: 'my_page', url: '/users/current_user'},
{name: '我的组织', key: 'my_organizes', url: '/users/current_user/organizes'}
]
personals.each { |ele|
Site.find_or_create_by(key: ele[:key]) do |site|
site.name = ele[:name]
site.url = ele[:url]
site.site_type = Site.site_types[:personal]
end
}
end
def self.set_common_menu!
commons = [
{name: '通知', key: 'notice', url: '/users/current_user/user_messages'},
{name: '找回密码', key: 'lost_password', url: '/account/lost_password'},
{name: '注册', key: 'register', url: '/login?login=false'}
]
commons.each { |ele|
Site.find_or_create_by(key: ele[:key]) do |site|
site.name = ele[:name]
site.url = ele[:url]
site.site_type = Site.site_types[:common]
end
}
end
end

55
app/models/team.rb Normal file
View File

@ -0,0 +1,55 @@
# == Schema Information
#
# Table name: teams
#
# id :integer not null, primary key
# organization_id :integer
# name :string(255)
# description :string(255)
# authorize :integer default("0")
# num_projects :integer default("0")
# num_users :integer default("0")
# includes_all_project :boolean default("0")
# can_create_org_project :boolean default("0")
# gtid :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_teams_on_organization_id (organization_id)
#
class Team < ApplicationRecord
belongs_to :organization
belongs_to :organization_extension, foreign_key: :organization_id, primary_key: :organization_id, counter_cache: :num_teams
has_many :team_projects, dependent: :destroy
has_many :team_units, dependent: :destroy
has_many :team_users, dependent: :destroy
validates :name, uniqueness: {scope: :organization_id}
enum authorize: {common: 0, read: 1, write: 2, admin: 3, owner: 4}
def self.build(organization_id, name, description, authorize, includes_all_project, can_create_org_project)
self.create!(organization_id: organization_id,
name: name,
description: description,
authorize: authorize,
includes_all_project: includes_all_project,
can_create_org_project: can_create_org_project)
end
def setup_team_project!
return unless includes_all_project
organization.projects.each do |project|
TeamProject.build(organization.id, id, project.id)
end
end
def is_member?(user_id)
team_users.where(user_id: user_id).present?
end
end

View File

@ -0,0 +1,30 @@
# == Schema Information
#
# Table name: team_projects
#
# id :integer not null, primary key
# organization_id :integer
# project_id :integer
# team_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_team_projects_on_organization_id (organization_id)
# index_team_projects_on_project_id (project_id)
# index_team_projects_on_team_id (team_id)
#
class TeamProject < ApplicationRecord
belongs_to :organization
belongs_to :project
belongs_to :team, counter_cache: :num_projects
validates :project_id, uniqueness: {scope: [:organization_id, :team_id]}
def self.build(organization_id, team_id, project_id)
self.find_or_create_by!(organization_id: organization_id, team_id: team_id, project_id: project_id)
end
end

31
app/models/team_unit.rb Normal file
View File

@ -0,0 +1,31 @@
# == Schema Information
#
# Table name: team_units
#
# id :integer not null, primary key
# organization_id :integer
# team_id :integer
# unit_type :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_team_units_on_organization_id (organization_id)
# index_team_units_on_team_id (team_id)
#
class TeamUnit < ApplicationRecord
belongs_to :organization
belongs_to :team
enum unit_type: {code: 1, issues: 2, pulls: 3, releases: 4}
validates :unit_type, uniqueness: { scope: [:organization_id, :team_id]}
def self.build(organization_id, team_id, unit_type)
self.create!(organization_id: organization_id, team_id: team_id, unit_type: unit_type)
end
end

30
app/models/team_user.rb Normal file
View File

@ -0,0 +1,30 @@
# == Schema Information
#
# Table name: team_users
#
# id :integer not null, primary key
# organization_id :integer
# team_id :integer
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_team_users_on_organization_id (organization_id)
# index_team_users_on_team_id (team_id)
# index_team_users_on_user_id (user_id)
#
class TeamUser < ApplicationRecord
belongs_to :organization
belongs_to :team, counter_cache: :num_users
belongs_to :user
validates :user_id, uniqueness: {scope: [:organization_id, :team_id]}
def self.build(organization_id, user_id, team_id)
self.create!(organization_id: organization_id, user_id: user_id, team_id: team_id)
end
end

View File

@ -58,14 +58,13 @@
# index_users_on_type (type)
#
class User < ApplicationRecord
class User < Owner
default_scope {where(type: %w(User AnonymousUser))}
extend Enumerize
include Watchable
include Likeable
include BaseModel
include ProjectOperable
include ProjectAbility
include Droneable
# include Searchable::Dependents::User
@ -136,9 +135,8 @@ class User < ApplicationRecord
has_many :attachments,foreign_key: :author_id, :dependent => :destroy
# 关注
has_many :be_watchers, foreign_key: :user_id, dependent: :destroy # 我的关注
has_many :be_watcher_users, through: :be_watchers, dependent: :destroy # 我关注的用户
has_many :watchers, as: :watchable, dependent: :destroy
# has_many :be_watchers, foreign_key: :user_id, dependent: :destroy # 我的关注
# has_many :be_watcher_users, through: :be_watchers, dependent: :destroy # 我关注的用户
has_one :ci_cloud_account, class_name: 'Ci::CloudAccount', dependent: :destroy
@ -154,21 +152,21 @@ class User < ApplicationRecord
# 项目
has_many :applied_projects, dependent: :destroy
has_many :projects, dependent: :destroy
has_many :repositories, dependent: :destroy
# 教学案例
# has_many :libraries, dependent: :destroy
has_many :project_trends, dependent: :destroy
has_many :oauths , dependent: :destroy
has_many :organization_users, dependent: :destroy
has_many :organizations, through: :organization_users
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }
scope :like, lambda { |keywords|
where("LOWER(concat(lastname, firstname, login, mail)) LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank?
}
scope :simple_select, -> {select(:id, :login, :lastname,:firstname, :nickname, :gitea_uid)}
scope :simple_select, -> {select(:id, :login, :lastname,:firstname, :nickname, :gitea_uid, :type)}
attr_accessor :password, :password_confirmation

View File

@ -17,15 +17,12 @@ class Projects::ListMyQuery < ApplicationQuery
projects = Project.visible
end
if params[:is_public].present?
projects = projects.is_private.members_projects(user.id) if params[:is_public].to_s == "private"
projects = projects.visible.members_projects(user.id) if params[:is_public].to_s == "public"
end
if params[:category].blank?
projects = projects.members_projects(user.id)
elsif params[:category].to_s == "join"
projects = projects.where.not(user_id: user.id).members_projects(user.id)
normal_projects = projects.where.not(user_id: user.id).members_projects(user.id).to_sql
org_projects = projects.joins(team_projects: [team: :team_users]).where(team_users: {user_id: user.id}).to_sql
projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
elsif params[:category].to_s == "manage"
projects = projects.where(user_id: user.id)
elsif params[:category].to_s == "watched" #我关注的
@ -39,6 +36,11 @@ class Projects::ListMyQuery < ApplicationQuery
# projects = projects.is_private.joins(:members).where(members: { user_id: user.id })
end
if params[:is_public].present?
projects = projects.is_private if params[:is_public].to_s == "private"
projects = projects.visible if params[:is_public].to_s == "public"
end
if params[:project_type].to_s === "common"
projects = projects.common
elsif params[:project_type].to_s === "mirror"

View File

@ -132,7 +132,8 @@ class Gitea::ClientService < ApplicationService
when 204
puts "[gitea] "
raise Error, "[gitea] delete ok"
# raise Error, "[gitea] delete ok"
{status: 204}
when 409
message = "创建失败,请检查该分支合并是否已存在"
raise Error, mark + message

View File

@ -0,0 +1,44 @@
class Gitea::Organization::CreateService < Gitea::ClientService
attr_reader :token, :org
def initialize(token, org)
@token = token
@org = org
end
def call
response = post(url, request_params)
render_status(response)
end
private
def request_params
create_params = {
username: org.login,
description: org.description,
location: org.location,
repo_admin_change_team_access: org.repo_admin_change_team_access,
visibility: visibility(org.visibility),
website: org.website,
max_repo_creation: org.max_repo_creation
}
Hash.new.merge(token: token, data: create_params)
end
def visibility(visibility)
case visibility
when "common"
"public"
when "limited"
"limited"
when "privacy"
"private"
else
"public"
end
end
def url
"/orgs".freeze
end
end

View File

@ -0,0 +1,23 @@
class Gitea::Organization::DeleteService < Gitea::ClientService
attr_reader :token, :name
def initialize(token, name)
@token = token
@name = name
end
def call
response = delete(url, params)
render_status(response)
end
private
def params
Hash.new.merge(token: token)
end
def url
"/orgs/#{name}".freeze
end
end

View File

@ -0,0 +1,21 @@
class Gitea::Organization::GetService < Gitea::ClientService
attr_reader :org
def initialize(org)
@org = org
end
def call
response = get(url, params)
render_status(response)
end
private
def params
Hash.new.merge(token: org.gitea_token)
end
def url
"/orgs/#{org.login}".freeze
end
end

View File

@ -0,0 +1,23 @@
class Gitea::Organization::OrganizationUser::DeleteService < Gitea::ClientService
attr_reader :token, :org_name, :username
def initialize(token, org_name, username)
@token = token
@org_name = org_name
@username = username
end
def call
response = delete(url, params)
render_status(response)
end
private
def params
Hash.new.merge(token: token)
end
def url
"/orgs/#{org_name}/members/#{username}"
end
end

View File

@ -0,0 +1,25 @@
class Gitea::Organization::Repository::CreateService < Gitea::ClientService
attr_reader :token, :org_name, :params
def initialize(token, org_name, params)
@token = token
@org_name = org_name
@params = params
end
def call
response = post(url, request_params)
render_201_response(response)
end
private
def request_params
create_params = params.merge(readme: "readme")
Hash.new.merge(token: token, data: create_params)
end
def url
"/orgs/#{org_name}/repos".freeze
end
end

View File

@ -0,0 +1,50 @@
class Gitea::Organization::Team::CreateService < Gitea::ClientService
attr_reader :token, :org, :team
def initialize(token, org, team)
@token = token
@org = org
@team = team
end
def call
response = post(url, request_params)
render_status(response)
end
private
def request_params
create_params = {
name: team.name,
description: team.description,
permission: permission(team.authorize),
includes_all_repositories: team.includes_all_project,
can_create_org_repo: team.can_create_org_project,
units: unit_arrays
}
Hash.new.merge(token: token, data: create_params)
end
def permission(authorize)
case authorize
when "read"
"read"
when "write"
"write"
when "admin"
"admin"
when "owner"
"owner"
else
"none"
end
end
def unit_arrays
team.team_units.pluck(:unit_type).collect{|t|"repo.#{t}"}
end
def url
"/orgs/#{org.login}/teams".freeze
end
end

View File

@ -0,0 +1,22 @@
class Gitea::Organization::Team::DeleteService < Gitea::ClientService
attr_reader :token, :gtid
def initialize(token, gtid)
@token = token
@gtid = gtid
end
def call
response = delete(url, params)
render_status(response)
end
private
def params
Hash.new.merge(token: token)
end
def url
"/teams/#{gtid}".freeze
end
end

View File

@ -0,0 +1,49 @@
class Gitea::Organization::Team::UpdateService < Gitea::ClientService
attr_reader :token, :team
def initialize(token, team)
@token = token
@team = team
end
def call
response = patch(url, request_params)
render_status(response)
end
private
def request_params
update_params = {
name: team.name,
description: team.description,
permission: permission(team.authorize),
includes_all_repositories: team.includes_all_project,
can_create_org_repo: team.can_create_org_project,
units: unit_arrays
}
Hash.new.merge(token: token, data: update_params)
end
def permission(authorize)
case authorize
when "read"
"read"
when "write"
"write"
when "admin"
"admin"
when "owner"
"owner"
else
"none"
end
end
def unit_arrays
team.team_units.pluck(:unit_type).collect{|t|"repo.#{t}"}
end
def url
"/teams/#{team.gtid}".freeze
end
end

View File

@ -0,0 +1,24 @@
class Gitea::Organization::TeamProject::CreateService < Gitea::ClientService
attr_reader :token, :gtid, :org_name, :repo_name
def initialize(token, gtid, org_name, repo_name)
@token = token
@gtid = gtid
@org_name = org_name
@repo_name = repo_name
end
def call
response = put(url, request_params)
render_status(response)
end
private
def request_params
Hash.new.merge(token: token)
end
def url
"/teams/#{gtid}/repos/#{org_name}/#{repo_name}".freeze
end
end

View File

@ -0,0 +1,24 @@
class Gitea::Organization::TeamProject::DeleteService < Gitea::ClientService
attr_reader :token, :gtid, :org_name, :repo_name
def initialize(token, gtid, org_name, repo_name)
@token = token
@gtid = gtid
@org_name = org_name
@repo_name = repo_name
end
def call
response = delete(url, params)
render_status(response)
end
private
def params
Hash.new.merge(token: token)
end
def url
"/teams/#{gtid}/repos/#{org_name}/#{repo_name}".freeze
end
end

View File

@ -0,0 +1,23 @@
class Gitea::Organization::TeamUser::CreateService < Gitea::ClientService
attr_reader :token, :gtid, :username
def initialize(token, gtid, username)
@token = token
@gtid = gtid
@username = username
end
def call
response = put(url, request_params)
render_status(response)
end
private
def request_params
Hash.new.merge(token: token)
end
def url
"/teams/#{gtid}/members/#{username}".freeze
end
end

View File

@ -0,0 +1,23 @@
class Gitea::Organization::TeamUser::DeleteService < Gitea::ClientService
attr_reader :token, :gtid, :username
def initialize(token, gtid, username)
@token = token
@gtid = gtid
@username = username
end
def call
response = delete(url, params)
render_status(response)
end
private
def params
Hash.new.merge(token: token)
end
def url
"/teams/#{gtid}/members/#{username}"
end
end

View File

@ -0,0 +1,45 @@
class Gitea::Organization::UpdateService < Gitea::ClientService
attr_reader :token, :login, :org
def initialize(token, login, org)
@token = token
@login = login
@org = org
end
def call
response = patch(url, request_params)
render_status(response)
end
private
def request_params
update_params = {
name: org.login,
description: org.description,
location: org.location,
repo_admin_change_team_access: org.repo_admin_change_team_access,
visibility: visibility(org.visibility),
website: org.website,
max_repo_creation: org.max_repo_creation
}
Hash.new.merge(token: token, data: update_params)
end
def visibility(visibility)
case visibility
when "common"
"public"
when "limited"
"limited"
when "privacy"
"private"
else
"public"
end
end
def url
"/orgs/#{login}".freeze
end
end

View File

@ -0,0 +1,22 @@
class Gitea::Repository::Contributors::GetService < Gitea::ClientService
attr_reader :owner, :repo_name
def initialize(owner, repo_name)
@owner = owner
@repo_name = repo_name
end
def call
response = get(url, params)
render_status(response)
end
private
def params
Hash.new.merge(token: owner.gitea_token)
end
def url
"/repos/#{owner.login}/#{repo_name}/contributors"
end
end

View File

@ -0,0 +1,25 @@
class Gitea::Repository::Files::GetService < Gitea::ClientService
attr_reader :owner, :repo_name, :ref, :q, :token
def initialize(owner, repo_name, ref, q=nil, token=nil)
@owner = owner
@repo_name = repo_name
@ref = ref
@q = q
@token = token
end
def call
response = get(url, params)
render_status(response)
end
private
def params
Hash.new.merge(token: token, ref: ref, q: q)
end
def url
"/repos/#{owner.login}/#{repo_name}/find"
end
end

View File

@ -0,0 +1,27 @@
class Gitea::Repository::TransferService < Gitea::ClientService
attr_reader :token, :owner, :repo, :new_owner
def initialize(token, owner, repo, new_owner)
@token = token
@owner = owner
@repo = repo
@new_owner = new_owner
end
def call
response = post(url, request_params)
render_status(response)
end
private
def request_params
transfer_params = {
new_owner: new_owner
}
Hash.new.merge(token: token, data: transfer_params)
end
def url
"/repos/#{owner}/#{repo}/transfer".freeze
end
end

View File

@ -18,7 +18,8 @@ class Gitea::Versions::CreateService < Gitea::ClientService
end
def call
post(url, request_params)
response = post(url, request_params)
render_status(response)
end
private

View File

@ -1,12 +1,13 @@
# Get a list of all commits from a repository
class Gitea::Versions::ListService < Gitea::ClientService
attr_reader :token, :user_name, :repo
attr_reader :token, :user_name, :repo, :args
# sha: SHA or branch to start listing commits from (usually 'master')
def initialize(token, user_name, repo)
def initialize(token, user_name, repo, args={})
@token = token
@user_name = user_name
@repo = repo
@args = args
end
def call
@ -16,7 +17,7 @@ class Gitea::Versions::ListService < Gitea::ClientService
private
def params
Hash.new.merge(token: token)
args.merge(token: token)
end
def url

View File

@ -0,0 +1,73 @@
class Organizations::CreateService < ApplicationService
attr_reader :user, :params
attr_accessor :organization, :gitea_organization, :owner_team
def initialize(user, params)
@user = user
@params = params
end
def call
Rails.logger.info("######Organization create_service begin######")
Rails.logger.info("######params #{params}######")
ActiveRecord::Base.transaction do
create_org_and_extension
create_owner_info
create_gitea_org
sync_gitea_info
Rails.logger.info("######Organization create_service end######")
end
@organization
end
private
def description
params[:description]
end
def website
params[:website]
end
def location
params[:location].present? ? params[:location] : nil
end
def repo_admin_change_team_access
params[:repo_admin_change_team_access].present? ? params[:repo_admin_change_team_access] : false
end
def visibility
params[:visibility].present? ? params[:visibility] : "common"
end
def max_repo_creation
params[:max_repo_creation].present? ? params[:max_repo_creation] : -1
end
def create_org_and_extension
@organization = Organization.build(params[:name], user.gitea_token)
org_extension = OrganizationExtension.build(organization.id, description, website,
location, repo_admin_change_team_access,
visibility, max_repo_creation)
end
def create_owner_info
@owner_team = Team.build(organization.id, "Owners", "", 4, true, true)
TeamUnit.unit_types.keys.each do |u_type|
TeamUnit.build(organization.id, owner_team.id, u_type)
end
OrganizationUser.build(organization.id, user.id)
TeamUser.build(organization.id, user.id, owner_team.id)
end
def create_gitea_org
@gitea_organization = Gitea::Organization::CreateService.call(@organization.gitea_token, organization)
end
def sync_gitea_info
organization.update!(gitea_uid: gitea_organization["id"])
owner_team.update!(gtid: gitea_organization["owner_team"]["id"])
end
end

View File

@ -0,0 +1,75 @@
class Organizations::Teams::CreateService < ApplicationService
attr_reader :user, :org, :params
attr_accessor :team, :gitea_team
def initialize(user, org, params)
@user = user
@org = org
@params = params
end
def call
Rails.logger.info("######Team create_service begin######")
Rails.logger.info("######params #{params}######")
ActiveRecord::Base.transaction do
create_team
create_units
create_gitea_team
sync_team_gtid
team.setup_team_project!
end
Rails.logger.info("######Team create_service end######")
team
end
private
def name
params[:name]
end
def description
params[:description]
end
def authorize
params[:authorize].present? ? params[:authorize] : "common"
end
def includes_all_project
params[:includes_all_project].present? ? params[:includes_all_project] : false
end
def can_create_org_project
params[:can_create_org_project].present? ? params[:can_create_org_project] : false
end
def create_team
@team = Team.build(org.id, name, description, authorize,
includes_all_project, can_create_org_project)
end
def units_params
%w(admin owner).include?(authorize) ? %w(code issues pulls releases) : params[:unit_types]
end
def create_units
return if units_params.blank?
begin
units_params.each do |unit|
TeamUnit.build(org.id, team.id, unit)
end
rescue
raise ActiveRecord::Rollback, "TeamUnit create error"
end
end
def create_gitea_team
@gitea_team = Gitea::Organization::Team::CreateService.call(org.gitea_token, org, team)
end
def sync_team_gtid
team.update!(gtid: gitea_team["id"])
end
end

View File

@ -0,0 +1,59 @@
class Organizations::Teams::UpdateService < ApplicationService
attr_reader :user, :team, :params
def initialize(user, team, params)
@user = user
@team = team
@params = params
end
def call
Rails.logger.info("######Team update_service begin######")
Rails.logger.info("######params #{params}######")
ActiveRecord::Base.transaction do
update_team(update_params)
update_units
team.reload
team.setup_team_project!
update_gitea_team
end
Rails.logger.info("######Team update_service end######")
team
end
private
def update_params
if team.authorize == "owner"
update_params = params.slice(:description)
else
update_params = params.slice(:name, :description, :authorize, :includes_all_project, :can_create_org_project)
end
update_params
end
def units_params
%w(admin owner).include?(team.authorize) ? %w(code issues pulls releases) : params[:unit_types]
end
def update_team(update_params)
team.update_attributes!(update_params)
end
def update_units
return if units_params.blank?
begin
team.team_units.map{|u|u.destroy!}
Rails.logger.info units_params
units_params.each do |unit|
TeamUnit.build(team&.organization&.id, team.id, unit)
end
rescue
raise ActiveRecord::Rollback, "TeamUnit update error"
end
end
def update_gitea_team
Gitea::Organization::Team::UpdateService.call(team&.organization&.gitea_token, team)
end
end

View File

@ -13,6 +13,7 @@ class Projects::CreateService < ApplicationService
ActiveRecord::Base.transaction do
if @project.save!
Project.update_common_projects_count!
ProjectUnit.init_types(@project.id)
Repositories::CreateService.new(user, @project, repository_params).call
else
Rails.logger.info("#############___________create_project_erros______###########{@project.errors.messages}")
@ -36,6 +37,7 @@ class Projects::CreateService < ApplicationService
is_public: repo_is_public,
ignore_id: params[:ignore_id],
license_id: params[:license_id],
website: params[:website],
identifier: params[:repository_name] #新增,hs
}
end

View File

@ -20,7 +20,7 @@ class Projects::ForkService < ApplicationService
clone_project.save!
new_repository = clone_project.repository
new_repository.user = @target_owner
new_repository.owner = @target_owner
new_repository.identifier = @project.identifier
new_repository.save!

View File

@ -9,6 +9,7 @@ class Projects::MigrateService < ApplicationService
def call
@project = Project.new(project_params)
if @project.save!
ProjectUnit.init_types(@project.id)
Project.update_mirror_projects_count!
Repositories::MigrateService.new(user, @project, repository_params).call
else
@ -44,7 +45,7 @@ class Projects::MigrateService < ApplicationService
hidden: project_secretion[:hidden],
identifier: params[:repository_name],
mirror_url: params[:clone_addr],
user_id: user.id,
user_id: params[:user_id],
login: params[:auth_username],
password: params[:auth_password],
is_mirror: params[:is_mirror]

View File

@ -0,0 +1,50 @@
class Projects::TransferService < ApplicationService
attr_accessor :project, :owner, :new_owner, :gitea_repo
def initialize(project, new_owner)
@project = project
@owner = project.owner
@new_owner = new_owner
end
def call
Rails.logger.info("###### Project transfer_service begin ######")
ActiveRecord::Base.transaction do
gitea_update_owner
update_owner
update_repo_url
update_visit_teams
end
Rails.logger.info("##### Project transfer_service end ######")
@project.reload
end
private
def update_owner
project.update!(user_id: new_owner.id)
end
def update_repo_url
project.repository.update!(url: @gitea_repo["clone_url"])
end
def update_visit_teams
if new_owner.is_a?(Organization)
new_owner.teams.where(includes_all_project: true).each do |team|
TeamProject.build(new_owner.id, team.id, project.id)
end
else
project.team_projects.each(&:destroy!)
end
end
def gitea_update_owner
begin
@gitea_repo = Gitea::Repository::TransferService.call(owner&.gitea_token, owner&.login, project.identifier, new_owner&.login)
rescue Exception => e
Rails.logger.info("##### Project transfer_service, gitea transfer error #{e}")
end
end
end

View File

@ -1,6 +1,7 @@
class PullRequests::CreateService < ApplicationService
attr_reader :current_user, :owner, :project, :params
attr_accessor :pull_issue, :pull_request
def initialize(current_user, owner, project, params)
@owner = owner
@ -10,13 +11,15 @@ class PullRequests::CreateService < ApplicationService
end
def call
validate!
save_pull_issue!
save_pull_request!
save_issue_tags_relates!
save_tiding!
save_project_trend!
save_custom_journal_detail!
ActiveRecord::Base.transaction do
validate!
save_pull_issue!
save_pull_request!
save_issue_tags_relates!
save_tiding!
save_project_trend!
save_custom_journal_detail!
end
[pull_request, gitea_pull_request]
end
@ -43,7 +46,7 @@ class PullRequests::CreateService < ApplicationService
end
def save_pull_issue!
pull_issue.save
pull_issue.save!
end
def pull_request
@ -51,7 +54,7 @@ class PullRequests::CreateService < ApplicationService
end
def save_pull_request!
pull_request.save
pull_request.save!
end
def save_issue_tags_relates!
@ -76,7 +79,7 @@ class PullRequests::CreateService < ApplicationService
end
def save_project_trend!
project_trend.save
project_trend.save!
end
def project_trend
@ -142,5 +145,7 @@ class PullRequests::CreateService < ApplicationService
raise "title参数不能为空" if @params[:title].blank?
raise "head参数不能为空" if @params[:head].blank?
raise "base参数不能为空" if @params[:base].blank?
raise @pull_issue.errors.full_messages.join(", ") unless pull_issue.valid?
raise @pull_request.errors.full_messages.join(", ") unless pull_request.valid?
end
end

View File

@ -1,5 +1,6 @@
class Repositories::CreateService < ApplicationService
attr_reader :user, :project, :params
attr_accessor :repository, :gitea_repository
def initialize(user, project, params)
@project = project
@ -10,10 +11,10 @@ class Repositories::CreateService < ApplicationService
def call
@repository = Repository.new(repository_params)
ActiveRecord::Base.transaction do
if @repository.save!
gitea_repository = Gitea::Repository::CreateService.new(user.gitea_token, gitea_repository_params).call
sync_project(@repository, gitea_repository)
sync_repository(@repository, gitea_repository)
if repository.save!
create_gitea_repository
sync_project
sync_repository
# if project.project_type == "common"
# chain_params = {
# type: "create",
@ -29,7 +30,7 @@ class Repositories::CreateService < ApplicationService
else
Rails.logger.info("#############___________create_repository_erros______###########{@repository.errors.messages}")
end
@repository
repository
end
rescue => e
puts "create repository service error: #{e.message}"
@ -38,16 +39,35 @@ class Repositories::CreateService < ApplicationService
private
def sync_project(repository, gitea_repository)
def create_gitea_repository
if project.owner.is_a?(User)
@gitea_repository = Gitea::Repository::CreateService.new(user.gitea_token, gitea_repository_params).call
elsif project.owner.is_a?(Organization)
@gitea_repository = Gitea::Organization::Repository::CreateService.call(user.gitea_token, project.owner.login, gitea_repository_params)
project.owner.teams.each do |team|
next unless team.includes_all_project
TeamProject.build(project.user_id, team.id, project.id)
end
create_manager_member
end
end
def create_manager_member
return if project.owner.is_owner?(user.id)
project.add_member!(user.id, "Manager")
end
def sync_project
if gitea_repository
project.update_columns(
gpid: gitea_repository["id"],
identifier: repository.identifier,
default_branch: gitea_repository["default_branch"],
forked_count: gitea_repository["forks_count"])
end
end
def sync_repository(repository, gitea_repository)
def sync_repository
repository.update_columns(url: remote_repository_url,) if gitea_repository
end

View File

@ -0,0 +1,68 @@
class Repositories::DetailService < ApplicationService
include Repository::LanguagesPercentagable
attr_reader :owner, :repo, :user
def initialize(owner, repo, user)
@owner = owner
@repo = repo
@user = user
end
def call
if @repo.project.educoder?
return {
repo: {},
release: [],
branch: [],
tag: [],
contributor: [],
language: {},
readme: {}
}
else
return {
repo: repo_suitable,
release: release_suitable,
branch: branch_suitable,
tag: tag_suitable,
contributor: contributor_suitable,
language: language_suitable,
readme: readme_suitable
}
end
end
private
def repo_suitable
Gitea::Repository::GetService.call(@owner, @repo.identifier)
end
def release_suitable
releases = Gitea::Versions::ListService.call(@owner.gitea_token, @owner.try(:login), @repo.try(:identifier), {page: 1, limit: 1})
releases.is_a?(Hash) && releases[:status] == -1 ? [] : releases
end
def branch_suitable
branches = Gitea::Repository::Branches::ListService.call(@owner, @repo.identifier)
branches.is_a?(Hash) && branches[:status] == :error ? [] : branches
end
def tag_suitable
tags = Gitea::Repository::Tags::ListService.call(@owner&.gitea_token, @owner.login, @repo.identifier)
tags.is_a?(Hash) && tags[:status] == -1 ? [] : tags
end
def contributor_suitable
contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repo.identifier)
contributors.is_a?(Hash) && contributors.key?(:status) ? [] : contributors
end
def language_suitable
result = Gitea::Repository::Languages::ListService.call(@owner.login, @repo.identifier, @user&.gitea_token)
result[:status] === :success ? hash_transform_precentagable(result[:body]) : nil
end
def readme_suitable
result = Gitea::Repository::Readme::GetService.call(@owner.login, @repo.identifier, @repo.default_branch, @owner.gitea_token)
result[:status] === :success ? result[:body] : nil
end
end

View File

@ -28,7 +28,7 @@ class Repositories::MigrateService < ApplicationService
{
clone_addr: params[:mirror_url],
repo_name: params[:identifier],
uid: user.gitea_uid,
uid: project&.owner&.gitea_uid,
private: params[:hidden],
mirror: wrapper_mirror || false,
auth_username: params[:login],

View File

@ -0,0 +1,2 @@
json.id secret['id']
json.name secret['name']

View File

@ -0,0 +1,3 @@
json.array! @secrets do |secret|
json.partial! "/ci/secrets/index", secret: secret
end

View File

@ -0,0 +1,8 @@
json.user_id user.id
json.login user.login
json.name user.full_name
json.mail user.mail
json.identity user.identity
json.watched current_user.watched?(user)
# json.email user.mail # 邮箱原则上不暴露的,如果实在需要的话只能对某些具体的接口公开
json.image_url url_to_avatar(user)

View File

@ -0,0 +1,7 @@
json.id org_user.id
json.user do
json.partial! "organizations/user_detail", user: org_user.user
end
json.team_names org_user.teams.pluck(:name)
json.created_at org_user.created_at.strftime("%Y-%m-%d")

View File

@ -0,0 +1,4 @@
json.total_count @organization_users.total_count
json.organization_users @organization_users do |org_user|
json.partial! "detail", org_user: org_user, organization: @organization
end

View File

@ -0,0 +1,13 @@
json.id organization.id
json.name organization.login
json.description organization.description
json.website organization.website
json.location organization.location
json.repo_admin_change_team_access organization.repo_admin_change_team_access
json.visibility organization.visibility
json.max_repo_creation organization.max_repo_creation
json.num_projects organization.num_projects
json.num_users organization.num_users
json.num_teams organization.num_teams
json.avatar_url url_to_avatar(organization)
json.created_at organization.created_on.strftime("%Y-%m-%d")

View File

@ -0,0 +1 @@
json.partial! "detail", organization: @organization

View File

@ -0,0 +1,4 @@
json.total_count @organizations.total_count
json.organizations @organizations do |organization|
json.partial! "detail", organization: organization
end

View File

@ -0,0 +1,4 @@
json.partial! "detail", organization: @organization
json.can_create_project @can_create_project
json.is_admin @is_admin
json.is_member @is_member

View File

@ -0,0 +1 @@
json.partial! "detail", organization: @organization

View File

@ -0,0 +1,9 @@
json.total_count @projects.total_count
json.projects @projects.each do |project|
json.(project, :id, :name, :identifier, :description, :forked_count, :praises_count, :forked_from_project_id)
json.mirror_url project.repository&.mirror_url
json.type project.numerical_for_project_type
json.praised project.praised_by?(current_user)
json.last_update_time render_unix_time(project.updated_on)
json.time_ago time_from_now(project.updated_on)
end

View File

@ -0,0 +1,9 @@
json.total_count @projects.size
json.projects @projects.each do |project|
json.(project, :id, :name, :identifier, :description, :forked_count, :praises_count, :forked_from_project_id)
json.mirror_url project.repository&.mirror_url
json.type project.numerical_for_project_type
json.praised project.praised_by?(current_user)
json.last_update_time render_unix_time(project.updated_on)
json.time_ago time_from_now(project.updated_on)
end

View File

@ -0,0 +1,7 @@
json.id team_project.id
json.project do
json.owner_name team_project&.project&.owner&.login
json.owner_image_url url_to_avatar(team_project&.project&.owner)
json.name team_project&.project&.name
json.identifier team_project&.project&.identifier
end

View File

@ -0,0 +1 @@
json.partial! "detail", team_project: @team_project, team: @team, organization: @organization

View File

@ -0,0 +1,4 @@
json.total_count @team_projects.total_count
json.team_projects @team_projects do |team_project|
json.partial! "detail", team_project: team_project, team: @team, organization: @organization
end

View File

@ -0,0 +1,5 @@
json.id team_user.id
json.user do
json.partial! "organizations/user_detail", user: team_user.user
end
json.created_at team_user.created_at.strftime("%Y-%m-%d")

View File

@ -0,0 +1 @@
json.partial! "detail", team_user: @team_user, team: @team, organization: @organization

View File

@ -0,0 +1,4 @@
json.total_count @team_users.total_count
json.team_users @team_users do |team_user|
json.partial! "detail", team_user: team_user, team: @team, organization: @organization
end

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