Compare commits

...

138 Commits

Author SHA1 Message Date
jasder 8c71a72fce FIX code bug 2021-04-23 17:53:59 +08:00
jasder ee7acaaa82 FIX update README.md 2021-04-23 17:32:45 +08:00
jasder c1d4df6e06 FIX release show time bug 2021-04-23 17:21:17 +08:00
jasder 55f7beb22d FIX update CHANGELOG.md 2021-04-23 17:19:28 +08:00
jasder aa314c141c ADD some url with in hovercoard api 2021-04-23 14:40:26 +08:00
jasder 37e1704897 ADD location column for user hovercard api 2021-04-22 18:10:20 +08:00
jasder ddacf2a733 FIX get organzition's name bug 2021-04-22 16:45:26 +08:00
jasder d07543111a FIX code bug 2021-04-22 16:42:50 +08:00
jasder 908cda56f3 ADD users/hovercard api 2021-04-22 16:36:40 +08:00
jasder 841778900c FIX code review 2021-04-22 16:35:54 +08:00
jasder 6a3d297b30 FIX code bug 2021-04-20 19:07:48 +08:00
jasder 002c773746 FIX code bug 2021-04-20 19:06:31 +08:00
jasder 45f05c9fc4 FIX render commmiter and author avatar bug 2021-04-20 18:58:14 +08:00
jasder 84e48d25c1 FIX render user bug with pull‘s commits and repo's commits api 2021-04-20 18:35:41 +08:00
jasder 37d8a5426f Merge branch 'develop' of https://git.trustie.net/jasder/forgeplus into develop 2021-04-20 11:14:03 +08:00
jasder df3bf621ce FIX 团队中的项目名称显示问题 2021-04-20 11:08:18 +08:00
vilet.yy da98efe04f add: org project field 2021-04-20 10:50:06 +08:00
vilet.yy 2976742b29 fix: org team nickname change error 2021-04-19 18:37:30 +08:00
vilet.yy 55e081ba2b fix 2021-04-19 18:37:30 +08:00
vilet.yy a3c49b3d5f add: org valid 2021-04-19 18:37:30 +08:00
vilet.yy 5a9f29c25f fix 2021-04-19 18:37:30 +08:00
vilet.yy f75827bcf0 fix: org not need create member 2021-04-19 18:37:30 +08:00
vilet.yy f0c4d1226b fix: org not need create member 2021-04-19 18:37:30 +08:00
vilet.yy 3a47c61010 add: team nickname 2021-04-19 18:37:30 +08:00
jasder 1f0388c1a1 Merge branch 'develop' of https://git.trustie.net/jasder/forgeplus into develop 2021-04-19 18:36:07 +08:00
jasder de6ae5b720 Upate models Schema Information 2021-04-19 18:36:02 +08:00
jasder 8a55e84372 FIX 调整自动生成头像中字母大小 2021-04-19 18:34:18 +08:00
jasder f055fed7d0 ADD 创建团队支持中文名称
创建组织团队使用中文名称报错、组织项目下创建issue报错
2021-04-19 18:10:31 +08:00
jasder 21befb6486 FIX auto generate avatar bug 2021-04-19 17:28:57 +08:00
jasder b6e8a135f0 FIX url_to_avatar bug 2021-04-19 15:42:54 +08:00
jasder 9cddda7fd4 Update render user avatar url in projects api 2021-04-19 15:38:51 +08:00
jasder 002ae9f327 ADD auto generate user avatar 2021-04-19 15:06:56 +08:00
vilet.yy 8055ae2c07 fix: org team nickname change error 2021-04-19 12:14:11 +08:00
vilet.yy 01f32ae2d7 fix 2021-04-19 12:09:38 +08:00
vilet.yy 1c6982d15b add: org valid 2021-04-19 12:07:12 +08:00
vilet.yy 9300483c1a fix 2021-04-19 11:40:38 +08:00
vilet.yy 1920cb5ce3 fix: org not need create member 2021-04-19 11:35:12 +08:00
vilet.yy 5a20842edb fix: org not need create member 2021-04-19 11:26:30 +08:00
vilet.yy 506ab1722d add: team nickname 2021-04-19 10:54:36 +08:00
jasder 07d11737e9 组织权限问题
组织权限问题
2021-04-13 16:39:50 +08:00
vilet.yy 1fe9ea126a fix: permission with organization 2021-04-13 11:53:06 +08:00
jasder dcd4457cf5 FIX 开放平台管理员可以访问所有团队信息 2021-04-13 10:16:18 +08:00
jasder 0bd6621821 项目增加课程链接 2021-04-08 18:35:49 +08:00
vilet.yy 04a1376820 add: project's lesson_url 2021-04-08 15:20:34 +08:00
jasder c3a1fb78ea 组织支持中文名称 2021-04-07 11:34:37 +08:00
vilet.yy cf224bc9fe add: project menulist resources 2021-04-07 11:29:44 +08:00
vilet.yy d6c3bcfaf1 add: org add nickname 2021-04-07 11:29:44 +08:00
vilet.yy a66e5b876d fix 2021-04-07 11:29:44 +08:00
vilet.yy b11fd350da fix: project permission 2021-04-07 11:29:44 +08:00
vilet.yy c417fa7369 add: project list author type 2021-04-07 11:29:44 +08:00
jasder e9a4370adc pull bug 2021-04-02 18:57:49 +08:00
jasder e6b8435037 FIX gug 2021-04-02 18:57:13 +08:00
vilet.yy c175fa1339 fix: commit author and fork create units 2021-04-02 18:53:53 +08:00
jasder 6ce4b26047 FIX api bug 2021-04-02 18:40:26 +08:00
jasder e022e429d4 FIX code bug 2021-04-02 18:22:21 +08:00
jasder 50fb8d3983 ADD sync user info api 2021-04-02 18:18:52 +08:00
jasder 02d26df172 FIX query builds api bug 2021-04-02 15:53:17 +08:00
jasder 254718e390 Update db/structure.sql 2021-04-01 20:33:05 +08:00
jasder 0b6ebb7b36 FIX get site url bug with setting api 2021-04-01 15:04:17 +08:00
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 11350bb76f Merge branch 'develop' of https://git.trustie.net/jasder/forgeplus into develop 2021-03-01 10:02:35 +08:00
jasder 50d81b2490 FIX schema 2021-02-09 11:49:47 +08:00
134 changed files with 3020 additions and 476 deletions

4
.gitignore vendored
View File

@ -36,6 +36,7 @@ public/react/yarn.lock
/.idea/*
# Ignore react node_modules
public/system/*
public/react/*
/public/react/.cache
/public/react/node_modules/
@ -82,4 +83,5 @@ docker/
educoder.sql
redis_data/
Dockerfile
dump.rdb
dump.rdb
.tags*

53
CHANGELOG.md Normal file
View File

@ -0,0 +1,53 @@
# Changelog
## [v3.0.2](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-04-23
* BUGFIXES
* Fix 解决部分用户头像不显示问题
* Fix 解决代码库模块中最左侧目录中的文件定位加载不准确的问题
* FIX 解决团队管理页面中项目链接错误问题
* Fix 解决markdown格式文件显示问题
* Fix 解决组织名下创建项目报错的问题
* Fix 解决组织名下的项目创建issue报错的问题
* Fix 解决组织名下创建团队提示信息信息显示错误问题
* Fix 解决点击组织图片时,链接加载错误问题
* Fix 修复查询版本库信息安全漏洞
* Fix 解决修复团队成员操作访问组织仓库报403错误的问题
* Fix 解决owners团队成员对仓库添加成功失败的问题
* ENHANCEMENTS
* ADD 自动生产用户头像功能
* ADD 创建组织支持中文名称
* ADD 创建团建支持中文名称
* ADD 组织名称统一显示中文名
* ADD 团队名称统一显示中文名
* ADD 用户头像悬浮时展示相关信息
* ADD 项目详情页添加实践课程链接入口
* ADD README文件页面添加添加目录导航功能
* UPDATE 升级改版底部footer信息
* UPDATE 升级用户操作版本库权限
## [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

@ -126,3 +126,5 @@ gem 'request_store'
gem 'harmonious_dictionary', '~> 0.0.1'
gem 'parallel', '~> 1.19', '>= 1.19.1'
gem 'letter_avatar'

View File

@ -162,6 +162,7 @@ GEM
activerecord
kaminari-core (= 1.2.0)
kaminari-core (1.2.0)
letter_avatar (0.3.8)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
@ -456,6 +457,7 @@ DEPENDENCIES
jbuilder (~> 2.5)
jquery-rails
kaminari (~> 1.1, >= 1.1.1)
letter_avatar
listen (>= 3.0.5, < 3.2)
mysql2 (>= 0.4.4, < 0.6.0)
oauth2

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+
* imagemagick
### Steps
#### 1. 克隆稳定版本
```
git clone -b standalone https://git.trustie.net/jasder/forgeplus.git
```
### 2. 配置初始化文件
#### 2. 安装依赖包
```bash
cd forgeplus && bundle install
```
#### 3. 配置初始化文件
进入项目根目录执行一下命令:
```bash
@ -27,12 +46,26 @@ 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服务步骤
**配置gitea服务步骤**
1. 部署gitea服务并注册root账户
2. 修改forge平台的 config/configuration.yml中的gitea服务指向地址
@ -44,75 +77,93 @@ gitea:
base_url: '/api/v1'
```
### 4. 安装redis环境
#### 6. 安装redis环境
**请自行搜索各平台如何安装部署redis环境**
### 5. 创建数据库
#### 7. 安装imagemagick插件
- Mac OS X
```bash
rails db:create
brew install imagemagick ghostscript
```
### 6. 导入数据表结构
- Linux
```bash
sudo apt-get install -y imagemagick
```
#### 8. 创建数据库
**开发环境为development 生成环境为production**
```bash
rails db:create RAILS_ENV=development
```
#### 9. 导入数据表结构
```bash
bundle exec rake sync_table_structure:import_csv
```
### 7. 执行migrate迁移文件
#### 10. 执行migrate迁移文件
**开发环境为development 生成环境为production**
```bash
rails db:migrate RAILS_ENV=development
```
### 8. 启动redis(此处已mac系统为例)
#### 11. clone前端代码
**将前端代码克隆到public/react目录下目录结构应该是: public/react/build**
```bash
git clone -b standalone https://git.trustie.net/jasder/build.git
```
#### 12. 启动redis(此处已mac系统为例)
```bash
redis-server&
```
### 9. 启动sidekiq
#### 13. 启动sidekiq
**开发环境为development 生成环境为production**
```bash
bundle exec sidekiq -C config/sidekiq.yml -e production -d
```
### 10. 启动rails服务
#### 14. 启动rails服务
```bash
rails s
```
### 11. 浏览器访问
#### 15. 浏览器访问
在浏览器中输入如下地址访问:
```bash
http://localhost:3000/
```
#### 16. 其他说明
通过页面注册都第一个用户为平台管理员用户
## 页面展示
- 代码库
![](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

@ -28,7 +28,7 @@ class ApplicationController < ActionController::Base
DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z)
OPENKEY = "79e33abd4b6588941ab7622aed1e67e8"
helper_method :current_user
helper_method :current_user, :base_url
# 所有请求必须合法签名
def check_sign
@ -770,6 +770,11 @@ class ApplicationController < ActionController::Base
@repository ||= load_project&.repository
end
def base_url
request.base_url
end
private
def object_not_found
uid_logger("Missing template or cant't find record, responding with 404")

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

@ -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

@ -47,7 +47,7 @@ class MembersController < ApplicationController
end
def member_exists?
@project.member?(params[:user_id])
@project.members.exists?(params[:user_id])
end
def operate!
@ -59,6 +59,6 @@ class MembersController < ApplicationController
end
def check_member_not_exists!
return render_result(1, "user_id为#{params[:user_id]}的用户还不是项目成员") unless member_exists?
return render_result(1, "user_id为#{params[:user_id]}的用户还不是项目成员") unless @project.member?(params[:user_id])
end
end

View File

@ -21,7 +21,7 @@ class Organizations::BaseController < ApplicationController
end
def team_not_found_condition
@team.team_users.where(user_id: current_user.id).blank? && !@organization.is_owner?(current_user.id)
!current_user&.admin? && @team.team_users.where(user_id: current_user.id).blank? && !@organization.is_owner?(current_user.id)
end
def user_mark
@ -31,4 +31,4 @@ class Organizations::BaseController < ApplicationController
def project_mark
params[:repo_name] || params[:id]
end
end
end

View File

@ -25,6 +25,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
def create
ActiveRecord::Base.transaction do
Organizations::CreateForm.new(organization_params).validate!
@organization = Organizations::CreateService.call(current_user, organization_params)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
end
@ -36,8 +37,10 @@ class Organizations::OrganizationsController < Organizations::BaseController
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))
@organization.login = organization_params[:name] if organization_params[:name].present?
@organization.nickname = organization_params[:nickname] if organization_params[:nickname].present?
@organization.save!
@organization.organization_extension.update_attributes!(organization_params.except(:name, :nickname))
Gitea::Organization::UpdateService.call(@organization.gitea_token, login, @organization.reload)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
end
@ -82,7 +85,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
def organization_params
params.permit(:name, :description, :website, :location,
:repo_admin_change_team_access, :visibility,
:max_repo_creation)
:max_repo_creation, :nickname)
end
def password

View File

@ -33,7 +33,10 @@ class Organizations::TeamsController < Organizations::BaseController
end
def create
@team = Organizations::Teams::CreateService.call(current_user, @organization, team_params)
ActiveRecord::Base.transaction do
Organizations::CreateTeamForm.new(team_params).validate!
@team = Organizations::Teams::CreateService.call(current_user, @organization, team_params)
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
@ -60,7 +63,7 @@ class Organizations::TeamsController < Organizations::BaseController
private
def team_params
params.permit(:name, :description, :authorize, :includes_all_project, :can_create_org_project, :unit_types => [])
params.permit(:name, :nickname, :description, :authorize, :includes_all_project, :can_create_org_project, :unit_types => [])
end
def load_organization

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

@ -2,16 +2,32 @@ 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("resources")) if @project.has_menu_permission("resources")
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)
@projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)
category_id = params[:category_id]
@total_count =
@ -44,7 +60,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 +89,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.except(:private).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 +112,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!
@ -162,7 +183,7 @@ class ProjectsController < ApplicationController
private
def project_params
params.permit(:user_id, :name, :description, :repository_name,
params.permit(:user_id, :name, :description, :repository_name, :website, :lesson_url,
:project_category_id, :project_language_id, :license_id, :ignore_id, :private)
end

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))
@ -139,7 +140,7 @@ class PullRequestsController < ApplicationController
end
def pr_merge
return render_forbidden("你没有权限操作.") if @project.reporter?(current_user)
return render_forbidden("你没有权限操作.") unless current_user.project_manager?(@project)
if params[:do].blank?
normal_status(-1, "请选择合并方式")

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,7 +103,13 @@ 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
@ -187,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,57 @@
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)
key.to_s === "url" ? append_http(reset_site_url(value)) : reset_site_url(value)
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

@ -2,9 +2,9 @@ class UsersController < ApplicationController
include ApplicationHelper
include Ci::DbConnectable
before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users]
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users]
before_action :require_login, only: %i[me list]
before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users, :hovercard]
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard]
before_action :require_login, only: %i[me list sync_user_info]
before_action :connect_to_ci_db, only: [:get_user_info]
skip_before_action :check_sign, only: [:attachment_show]
@ -56,6 +56,9 @@ class UsersController < ApplicationController
@watchers = paginate(watchers)
end
def hovercard
end
def update
@user = User.find params[:id]
@user.update!(user_params)
@ -233,6 +236,26 @@ class UsersController < ApplicationController
render_ok
end
def sync_user_info
user = User.find_by_login params[:login]
return render_forbidden unless user === current_user
sync_params = {
email: params[:email],
password: params[:password]
}
Users::UpdateInfoForm.new(sync_params.merge(login: params[:login])).validate!
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params)
if interactor.success?
user.update!(password: params[:password], mail: params[:email], status: User::STATUS_EDIT_INFO)
render_ok
else
render_error(interactor.error)
end
end
private
def load_user
@user = User.find_by_login(params[:id]) || User.find_by(id: params[:id])

View File

@ -8,7 +8,7 @@ class VersionReleasesController < ApplicationController
version_releases = Gitea::Versions::ListService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier)).call
@version_releases = version_releases
@user_permission = current_user.present? && (current_user == @user || current_user.admin?)
@forge_releases = @repository.version_releases.select(:id,:version_gid).includes(:attachments)
@forge_releases = @repository.version_releases.select(:id,:version_gid, :created_at).includes(:attachments)
end
def new

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

@ -0,0 +1,8 @@
class Organizations::CreateForm < BaseForm
NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
attr_accessor :name, :description, :website, :location, :repo_admin_change_team_access, :visibility, :max_repo_creation, :nickname
validates :name, :nickname, :visibility, presence: true
validates :name, format: { with: NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
end

View File

@ -0,0 +1,8 @@
class Organizations::CreateTeamForm < BaseForm
NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
attr_accessor :name, :nickname, :description, :authorize, :includes_all_project, :can_create_org_project, :unit_types
validates :name, :nickname, :authorize, presence: true
validates :name, format: { with: NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
end

View File

@ -0,0 +1,9 @@
class Users::UpdateInfoForm
include ActiveModel::Model
attr_accessor :email, :password, :login
validates :email, presence: true, format: { with: CustomRegexp::EMAIL }
validates :password, presence: true
validates :login, presence: true
end

View File

@ -35,12 +35,6 @@ module ApplicationHelper
course.course_modules.find_by(module_type: "graduation").try(:id)
end
# 是否关注
# from_user_id为被关注的用户
def follow?(from_user_id, user_id)
Watcher.where(watchable_type: 'Principal', watchable_id: from_user_id, user_id: user_id).exists?
end
# git用户
# git用户命名规则login+"@educoder.net"
def git_username(email)
@ -144,17 +138,12 @@ module ApplicationHelper
if File.exist?(disk_filename(source&.class, source&.id))
ctime = File.ctime(disk_filename(source.class, source.id)).to_i
if %w(User Organization).include?(source.class.to_s)
File.join(relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
File.join("images", relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
else
File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
end
elsif source.class.to_s == 'User'
str = source.user_extension.try(:gender).to_i == 0 ? "b" : "g"
File.join(relative_path, "#{source.class}", str)
elsif source.class.to_s == 'Subject'
File.join("images","educoder", "index", "subject", "subject#{rand(17)}.jpg")
elsif source.class.to_s == 'Shixun'
File.join("images","educoder", "index", "shixun", "shixun#{rand(23)}.jpg")
source.get_letter_avatar_url
end
end

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
@ -85,8 +90,4 @@ module ProjectsHelper
def render_educoder_avatar_url(project_educoder)
[Rails.application.config_for(:configuration)['educoder']['cdn_url'], project_educoder&.image_url].join('/')
end
def render_avatar_url(owner)
['images', url_to_avatar(owner)].join('/')
end
end

View File

@ -1,7 +1,6 @@
module RepositoriesHelper
def render_permission(user, project)
return "Admin" if user&.admin?
return "Owner" if user === project.owner
project.get_premission(user)
end
@ -28,39 +27,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

@ -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

@ -17,7 +17,7 @@
# disk_directory :string(255)
# attachtype :integer default("1")
# is_public :integer default("1")
# copy_from :integer
# copy_from :string(255)
# quotes :integer default("0")
# is_publish :integer default("1")
# publish_time :datetime
@ -26,15 +26,15 @@
# cloud_url :string(255) default("")
# course_second_category_id :integer default("0")
# delay_publish :boolean default("0")
# link :string(255)
# clone_id :integer
#
# Indexes
#
# index_attachments_on_author_id (author_id)
# index_attachments_on_clone_id (clone_id)
# index_attachments_on_container_id_and_container_type (container_id,container_type)
# index_attachments_on_course_second_category_id (course_second_category_id)
# index_attachments_on_created_on (created_on)
# index_attachments_on_is_public (is_public)
# index_attachments_on_quotes (quotes)
#
class Attachment < ApplicationRecord

View File

@ -1,3 +1,21 @@
# == Schema Information
#
# Table name: chart_rules
#
# id :integer not null, primary key
# rule_type :string(255)
# content :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
# competition_id :integer
# competition_stage_id :integer
#
# Indexes
#
# index_chart_rules_on_competition_id (competition_id)
# index_chart_rules_on_competition_stage_id (competition_stage_id)
#
class ChartRule < ApplicationRecord
validates :content, length: { maximum: 1000, too_long: "不能超过1000个字符" }

View File

@ -1,3 +1,22 @@
# == Schema Information
#
# Table name: stages
#
# id :integer not null
# subject_id :integer
# name :string(255)
# description :text(65535)
# user_id :integer
# position :integer
# created_at :datetime not null
# updated_at :datetime not null
# shixuns_count :integer default("0")
#
# Indexes
#
# index_stages_on_subject_id (subject_id)
#
class Ci::Stage < Ci::RemoteBase
self.primary_key = 'stage_id'

View File

@ -39,13 +39,14 @@
# 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")
# admin_visitable :boolean default("0")
# collaborator :boolean default("0")
# gitea_uid :integer
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
# gitea_token :string(255)
#
# Indexes
#
@ -53,8 +54,9 @@
# 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_login (login) UNIQUE
# index_users_on_mail (mail) UNIQUE
# index_users_on_phone (phone) UNIQUE
# index_users_on_type (type)
#

View File

@ -89,4 +89,7 @@ module ProjectOperable
member.member_roles.create!(role: role)
end
def has_menu_permission(unit_type)
self.project_units.where(unit_type: unit_type).exists?
end
end

View File

@ -6,6 +6,7 @@ module Watchable
has_many :watcher_users, through: :watchers, source: :user, validate: false
scope :watched_by, -> (user_id) { includes(:watchers).where(watchers: { user_id: user_id }) }
scope :following, -> (user_id) { watched_by(user_id) }
end
def watched?(watchable)
@ -21,6 +22,24 @@ module Watchable
obj.destroy! if obj.present?
end
# 我正在关注的、我追随的
def following
User.following(self.id)
end
def following_count
following.size
end
# 关注我的、我的粉丝、我的追随者
def followers
watcher_users
end
def followers_count
followers.size
end
module ClassMethods
end
end

View File

@ -1,3 +1,38 @@
# == Schema Information
#
# Table name: discusses
#
# id :integer not null, primary key
# user_id :integer
# dis_type :string(255)
# dis_id :integer
# content :text(65535)
# parent_id :integer
# root_id :integer
# praise_count :integer
# created_at :datetime not null
# updated_at :datetime not null
# challenge_id :integer
# reward :integer
# hidden :boolean default("0")
# last_reply_id :integer
# position :integer
# praises_count :integer default("0")
# sticky :boolean default("0")
# course_sticky :boolean default("0")
# course_hidden :boolean default("0")
# copy_message_id :integer
#
# Indexes
#
# index_discusses_on_challenge_id (challenge_id)
# index_discusses_on_copy_message_id (copy_message_id)
# index_discusses_on_course_hidden (course_hidden)
# index_discusses_on_course_sticky (course_sticky)
# index_discusses_on_dis_id_and_dis_type (dis_id,dis_type)
# index_discusses_on_user_id (user_id)
#
class Discuss < ApplicationRecord
default_scope { order(created_at: :desc) }

View File

@ -1,5 +1,24 @@
# == Schema Information
#
# Table name: forge_activities
#
# id :integer not null, primary key
# user_id :integer
# project_id :integer
# forge_act_id :integer
# forge_act_type :string(255)
# org_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# forge_act_index (project_id,forge_act_id,created_at,forge_act_type)
# index_forge_activities_on_forge_act_id (forge_act_id)
#
class ForgeActivity < ApplicationRecord
belongs_to :user
belongs_to :project
belongs_to :forge_act, polymorphic: true
end
end

View File

@ -1,3 +1,17 @@
# == Schema Information
#
# Table name: helps
#
# id :integer not null, primary key
# created_at :datetime not null
# updated_at :datetime not null
# about_us :text(65535)
# agreement :text(65535)
# status :text(65535)
# help_center :text(65535)
# join_us :text(65535)
#
class Help < ApplicationRecord
end
end

View File

@ -10,6 +10,7 @@
# sync_course :boolean default("0")
# sync_subject :boolean default("0")
# sync_shixun :boolean default("0")
# is_local :boolean default("0")
#
# Indexes
#

View File

@ -7,7 +7,6 @@
# content :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
# is_secret :boolean default("0")
#
class License < ApplicationRecord

View File

@ -1,3 +1,27 @@
# == Schema Information
#
# Table name: live_links
#
# id :integer not null, primary key
# course_id :integer
# user_id :integer
# url :string(255)
# description :text(65535)
# on_status :boolean default("0")
# created_at :datetime not null
# updated_at :datetime not null
# course_name :string(255)
# platform :string(255)
# live_time :datetime
# duration :integer
# position :integer
#
# Indexes
#
# index_live_links_on_course_id (course_id)
# index_live_links_on_user_id (user_id)
#
class LiveLink < ApplicationRecord
# belongs_to :course
belongs_to :user

View File

@ -11,7 +11,6 @@
# course_group_id :integer default("0")
# is_collect :integer default("1")
# graduation_group_id :integer default("0")
# is_apply_signature :boolean default("0")
#
# Indexes
#

View File

@ -1,3 +1,18 @@
# == Schema Information
#
# Table name: message_details
#
# id :integer not null, primary key
# content :text(4294967295)
# message_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_message_details_on_message_id (message_id)
#
class MessageDetail < ApplicationRecord
# belongs_to :message, :touch => true
validates :content, length: { maximum: 10000, too_long: "内容不能超过10000个字符" }

View File

@ -39,13 +39,14 @@
# 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")
# admin_visitable :boolean default("0")
# collaborator :boolean default("0")
# gitea_uid :integer
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
# gitea_token :string(255)
#
# Indexes
#
@ -53,8 +54,9 @@
# 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_login (login) UNIQUE
# index_users_on_mail (mail) UNIQUE
# index_users_on_phone (phone) UNIQUE
# index_users_on_type (type)
#
@ -78,8 +80,8 @@ class Organization < Owner
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)
def self.build(name, nickname, gitea_token=nil)
self.create!(login: name, nickname: nickname, gitea_token: gitea_token)
end
def can_create_project?(user_id)
@ -113,7 +115,9 @@ class Organization < Owner
end
def real_name
login
name = lastname + firstname
name = name.blank? ? (nickname.blank? ? login : nickname) : name
name.gsub(/\s+/, '').strip #6.11 -hs
end
def show_real_name

View File

@ -37,8 +37,6 @@
# rep_identifier :string(255)
# project_category_id :integer
# project_language_id :integer
# license_id :integer
# ignore_id :integer
# praises_count :integer default("0")
# watchers_count :integer default("0")
# issues_count :integer default("0")
@ -52,6 +50,11 @@
# open_devops_count :integer default("0")
# recommend :boolean default("0")
# platform :integer default("0")
# license_id :integer
# ignore_id :integer
# default_branch :string(255) default("master")
# website :string(255)
# lesson_url :string(255)
#
# Indexes
#
@ -68,7 +71,6 @@
# index_projects_on_updated_on (updated_on)
#
class Project < ApplicationRecord
include Matchable
include Publicable
@ -109,12 +111,15 @@ class Project < ApplicationRecord
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)
@ -153,6 +158,7 @@ class Project < ApplicationRecord
#创建项目管理员
def check_project_members
return if owner.is_a?(Organization)
unless members.present? && members.exists?(user_id: self.user_id)
member_params = {
user_id: self.user_id,
@ -234,10 +240,12 @@ class Project < ApplicationRecord
end
def get_premission user
permission = "Reporter"
member = members.find_by(user: user)
return "Owner" if owner?(user)
return "Manager" if manager?(user)
return "Developer" if develper?(user)
return "Reporter" if reporter?(user)
member&.roles&.last&.name || permission
return ""
end
def fork_project

View File

@ -8,11 +8,6 @@
# projects_count :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# ancestry :string(255)
#
# Indexes
#
# index_project_categories_on_ancestry (ancestry)
#
class ProjectCategory < ApplicationRecord

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, resources: 6}
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

@ -25,7 +25,6 @@
#
# Indexes
#
# index_repositories_on_identifier (identifier)
# index_repositories_on_project_id (project_id)
# index_repositories_on_user_id (user_id)
#
@ -41,6 +40,8 @@ class Repository < ApplicationRecord
validates :identifier, presence: true
delegate :default_branch, to: :project, allow_nil: true
def to_param
self.identifier.parameterize
end

View File

@ -1,3 +1,39 @@
# == Schema Information
#
# Table name: schools
#
# id :integer not null, primary key
# name :string(255)
# province :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# logo_link :string(255)
# pinyin :string(255)
# school_type :integer default("0")
# city :string(255)
# address :string(255)
# auto_users_trial :boolean default("0")
# shool_code :string(255)
# authorization_time :datetime
# ec_auth :integer default("0")
# identifier :string(255)
# is_online :boolean default("0")
# video_name :string(255)
# video_desc :string(255)
# course_link :string(255)
# course_name :string(255)
# partner_id :integer
# customer_id :integer
# school_property_id :integer
#
# Indexes
#
# index_schools_on_customer_id (customer_id)
# index_schools_on_identifier (identifier)
# index_schools_on_partner_id (partner_id)
# index_schools_on_school_property_id (school_property_id)
#
class School < ApplicationRecord
has_many :departments, dependent: :destroy

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

View File

@ -14,6 +14,7 @@
# gtid :integer
# created_at :datetime not null
# updated_at :datetime not null
# nickname :string(255)
#
# Indexes
#

View File

@ -1,4 +1,87 @@
# == Schema Information
#
# Table name: courses
#
# id :integer not null, primary key
# tea_id :integer
# name :string(255)
# state :integer
# code :string(255)
# time :integer
# extra :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# location :string(255)
# term :string(255)
# string :string(255)
# password :string(255)
# setup_time :string(255)
# endup_time :string(255)
# class_period :integer default("0")
# school_id :integer
# description :text(65535)
# status :integer default("1")
# attachmenttype :integer default("2")
# lft :integer
# rgt :integer
# is_public :integer default("1")
# inherit_members :integer default("1")
# open_student :integer default("0")
# outline :integer default("0")
# publish_resource :integer default("0")
# is_delete :integer default("0")
# end_time :integer
# end_term :string(255)
# is_excellent :integer default("0")
# excellent_option :integer default("0")
# is_copy :integer default("0")
# visits :integer default("0")
# syllabus_id :integer
# invite_code :string(255)
# qrcode :string(255)
# qrcode_expiretime :integer default("0")
# invite_code_halt :integer default("0")
# os_allow :integer default("0")
# credit :float(24)
# is_end :boolean default("0")
# end_date :date
# choose_group_allow :boolean default("0")
# homepage_show :boolean default("0")
# course_list_id :integer
# members_count :integer default("0")
# homework_commons_count :integer default("0")
# show_unit :boolean default("0")
# teacher_list :string(255) default("老师")
# student_list :string(255) default("学生")
# is_hidden :boolean default("0")
# course_members_count :integer default("0")
# course_groups_count :integer default("0")
# authentication :boolean default("0")
# professional_certification :boolean default("0")
# graduation_topics_count :integer default("0")
# graduation_tasks_count :integer default("0")
# polls_count :integer default("0")
# exercises_count :integer default("0")
# start_date :date
# subject_id :integer default("0")
# excellent :boolean default("0")
# email_notify :boolean default("0")
# sticky :boolean default("0")
# sticky_time :datetime
# laboratory_id :integer
# mooc_course_id :integer
#
# Indexes
#
# index_courses_on_invite_code (invite_code) UNIQUE
# index_courses_on_laboratory_id (laboratory_id)
# index_courses_on_mooc_course_id (mooc_course_id)
# index_courses_on_school_id_and_is_delete (school_id,is_delete)
# index_courses_on_subject_id (subject_id)
# index_courses_on_tea_id (tea_id)
#
class Trustie::Course < Trustie::Database
has_many :course_groups, class_name: "Trustie::CourseGroup"
has_many :homework_commons, class_name: "Trustie::HomeworkCommon"
end
end

View File

@ -1,3 +1,25 @@
# == Schema Information
#
# Table name: course_groups
#
# id :integer not null, primary key
# name :string(255)
# course_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# members_count :integer
# invite_code :string(255)
# position :integer default("0")
# course_members_count :integer default("0")
# invite_code_halt :boolean default("0")
# invite_code_set :integer default("0")
#
# Indexes
#
# index_course_groups_on_course_id (course_id)
# index_course_groups_on_invite_code (invite_code) UNIQUE
#
class Trustie::CourseGroup < Trustie::Database
belongs_to :course, class_name: "Trustie::Course"
end
end

View File

@ -1,3 +1,54 @@
# == Schema Information
#
# Table name: homework_commons
#
# id :integer not null, primary key
# name :string(255)
# user_id :integer
# description :text(65535)
# publish_time :datetime
# end_time :datetime
# homework_type :integer default("1")
# late_penalty :string(255) default("0")
# course_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# teacher_priority :integer default("1")
# anonymous_comment :boolean default("0")
# quotes :integer default("0")
# is_open :integer default("0")
# simi_time :datetime
# score_open :boolean default("0")
# anonymous_appeal :boolean default("0")
# homework_bank_id :integer
# is_update :boolean default("0")
# is_public :boolean default("0")
# reference_answer :text(65535)
# answer_public :boolean default("0")
# archive_time :datetime
# allow_late :boolean default("0")
# late_time :datetime
# work_public :boolean default("0")
# explanation :text(65535)
# unified_setting :boolean default("1")
# comment_public :boolean default("1")
# course_homework_category_id :integer
# work_efficiency :boolean default("0")
# eff_score :float(24) default("0")
# max_efficiency :float(24) default("0")
# course_second_category_id :integer default("0")
# calculation_time :datetime
# position :integer default("0")
# total_score :float(24) default("100")
# category_position :integer default("0")
#
# Indexes
#
# index_homework_commons_on_course_id_and_id (course_id,id)
# index_homework_commons_on_course_second_category_id (course_second_category_id)
# index_homework_commons_on_homework_bank_id (homework_bank_id)
#
class Trustie::HomeworkCommon < Trustie::Database
belongs_to :course, class_name: "Trustie::Course"
end
end

View File

@ -39,13 +39,14 @@
# 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")
# admin_visitable :boolean default("0")
# collaborator :boolean default("0")
# gitea_uid :integer
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
# gitea_token :string(255)
#
# Indexes
#
@ -53,8 +54,9 @@
# 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_login (login) UNIQUE
# index_users_on_mail (mail) UNIQUE
# index_users_on_phone (phone) UNIQUE
# index_users_on_type (type)
#
@ -66,6 +68,7 @@ class User < Owner
include Likeable
include BaseModel
include Droneable
include User::Avatar
# include Searchable::Dependents::User
# devops step
@ -134,10 +137,6 @@ class User < Owner
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_one :ci_cloud_account, class_name: 'Ci::CloudAccount', dependent: :destroy
# 认证
@ -189,6 +188,10 @@ class User < Owner
validate :validate_sensitive_string
validate :validate_password_length
def name
login
end
# 删除自动登录的token一旦退出下次会提示需要登录
def delete_autologin_token(value)
Token.where(:user_id => id, :action => 'autologin', :value => value).delete_all
@ -203,7 +206,7 @@ class User < Owner
end
def project_manager?(project)
project.managers.exists?(user: self) || self.admin?
project.manager?(self) || self.admin?
end
# 学号

43
app/models/user/avatar.rb Normal file
View File

@ -0,0 +1,43 @@
require 'letter_avatar/has_avatar'
require 'chinese_pinyin'
class User
module Avatar
extend ActiveSupport::Concern
include LetterAvatar::HasAvatar
def username
self.lastname.blank? ? self.login : Pinyin.t(self.lastname)
end
def get_letter_avatar_url(size = :lg)
avatar_path(self.username, size).split('public/')&.last
end
def self.get_letter_avatar_url(name)
return "" if name.blank?
avatar = LetterAvatar.generate Pinyin.t(name), 120
avatar.split('public/')&.last
end
def avatar_path(username, size)
LetterAvatar.generate username, avatar_size(size)
end
# 返回头像尺寸
# xs: 22px
# sm: 32px
# md: 48px
# lg: 120px
def avatar_size(size)
case size
when :xs then 22
when :sm then 32
when :md then 48
when :lg then 120
else size
end
end
end
end

View File

@ -12,7 +12,9 @@
#
# Indexes
#
# index_user_actions_on_ip (ip)
# index_user_actions_on_ip (ip)
# index_user_actions_on_user_id (user_id)
# index_user_actions_on_user_id_and_action_type (user_id,action_type)
#
class UserAction < ApplicationRecord

View File

@ -10,10 +10,13 @@
# updated_at :datetime not null
# register_status :integer default("0")
# action_status :integer default("0")
# is_delete :boolean default("0")
# user_id :integer
#
# Indexes
#
# index_user_agents_on_ip (ip) UNIQUE
# index_user_agents_on_ip (ip)
# index_user_agents_on_user_id (user_id)
#
class UserAgent < ApplicationRecord

View File

@ -22,6 +22,9 @@
# school_id :integer
# description :string(255) default("")
# department_id :integer
# honor :text(65535)
# edu_background :integer
# edu_entry_year :integer
#
# Indexes
#

View File

@ -25,7 +25,7 @@ class Ci::Builds::ListQuery < ApplicationQuery
scope
end
builds = scope.by_branch(params[:branch]) if params[:branch]
builds = builds.by_branch(params[:branch]) if params[:branch]
custom_sort(builds, params[:sort_by], params[:sort_direction])
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

@ -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

@ -47,7 +47,7 @@ class Organizations::CreateService < ApplicationService
end
def create_org_and_extension
@organization = Organization.build(params[:name], user.gitea_token)
@organization = Organization.build(params[:name], params[:nickname], user.gitea_token)
org_extension = OrganizationExtension.build(organization.id, description, website,
location, repo_admin_change_team_access,
visibility, max_repo_creation)

View File

@ -27,7 +27,7 @@ class Organizations::Teams::UpdateService < ApplicationService
if team.authorize == "owner"
update_params = params.slice(:description)
else
update_params = params.slice(:name, :description, :authorize, :includes_all_project, :can_create_org_project)
update_params = params.slice(:name, :nickname, :description, :authorize, :includes_all_project, :can_create_org_project)
end
update_params
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

@ -24,6 +24,8 @@ class Projects::ForkService < ApplicationService
new_repository.identifier = @project.identifier
new_repository.save!
ProjectUnit.init_types(clone_project.id)
result = Gitea::Repository::ForkService.new(@project.owner, @target_owner, @project.identifier, @organization).call
@project.update_column('forked_count', @project&.forked_count.to_i + 1)

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

View File

@ -62,6 +62,7 @@ class Repositories::CreateService < ApplicationService
project.update_columns(
gpid: gitea_repository["id"],
identifier: repository.identifier,
default_branch: gitea_repository["default_branch"],
forked_count: gitea_repository["forks_count"])
end
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

@ -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

@ -1,5 +1,6 @@
json.id organization.id
json.name organization.login
json.nickname organization.nickname.blank? ? organization.name : organization.nickname
json.description organization.description
json.website organization.website
json.location organization.location

View File

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

View File

@ -1,5 +1,6 @@
json.id team.id
json.name team.name
json.nickname team.nickname.blank? ? team.name : team.nickname
json.description team.description
json.authorize team.authorize
json.includes_all_project team.includes_all_project

View File

@ -26,7 +26,7 @@ json.author do
json.name user.try(:show_real_name)
json.type user&.type
json.login user.login
json.image_url render_avatar_url(user)
json.image_url url_to_avatar(user)
end
end

View File

@ -25,9 +25,10 @@ json.projects @projects do |project|
json.image_url render_educoder_avatar_url(project.project_educoder)
else
user = project.owner
json.type user.type
json.name user.try(:show_real_name)
json.login user.login
json.image_url render_avatar_url(user)
json.image_url url_to_avatar(user)
end
end

View File

@ -0,0 +1 @@
json.array! @project_units.pluck(:unit_type)

View File

@ -5,3 +5,5 @@ json.description @project.description
json.project_category_id @project.project_category_id
json.project_language_id @project.project_language_id
json.is_public @project.is_public
json.website @project.website
json.lesson_url @project.lesson_url

View File

@ -1,11 +1,11 @@
json.author do
author = User.find_by(login: commit['Author']['Name'])
json.partial! 'repositories/commit_author', locals: { user: author }
author = User.find_by(mail: commit['Author']['Email'])
json.partial! 'repositories/commit_author', locals: { user: author, name: commit['Committer']['Name'] }
end
json.committer do
author = User.find_by(login: commit['Committer']['Name'])
json.partial! 'repositories/commit_author', locals: { user: author }
author = User.find_by(mail: commit['Committer']['Email'])
json.partial! 'repositories/commit_author', locals: { user: author, name: commit['Committer']['Name'] }
end
json.timestamp render_unix_time(commit['Committer']['When'])
json.time_from_now time_from_now(commit['Committer']['When'])

View File

@ -1,5 +1,6 @@
json.author do
if @project.forge?
json.id user.id
json.login user.login
json.type user&.type
json.name user.real_name

View File

@ -26,9 +26,9 @@ if @project.forge?
end
json.author do
json.partial! 'commit_author', user: render_commit_author(commit['author'])
json.partial! 'commit_author', user: render_commit_author(commit['commit']['author']), name: commit['commit']['author']['name']
end
json.committer do
json.partial! 'commit_author', user: render_commit_author(commit['committer'])
json.partial! 'commit_author', user: render_commit_author(commit['commit']['committer']), name: commit['commit']['committer']['name']
end
end

View File

@ -4,5 +4,8 @@ if user
json.name user.real_name
json.image_url url_to_avatar(user)
else
json.nil!
json.id nil
json.login name
json.name name
json.image_url User::Avatar.get_letter_avatar_url(name)
end

View File

@ -6,10 +6,14 @@ else
json.commits do
json.array! @hash_commit[:body] do |commit|
commiter = commit['committer']
if commiter.present?
commit_user_id = commiter['id']
forge_user = User.simple_select.find_by(gitea_uid: commit_user_id)
end
forge_user =
if commiter.present?
User.simple_select.find_by(mail: commiter['email'])
else
User.simple_select.find_by(mail: commit['commit']['committer']['email'])
end
json.sha commit['sha']
json.message commit['commit']['message']
json.timestamp render_unix_time(commit['commit']['author']['date'])
@ -17,7 +21,13 @@ else
if forge_user
json.partial! 'author', user: forge_user
else
json.author nil
json.author do
json.id nil
json.login commit['commit']['author']['name']
json.type nil
json.name commit['commit']['author']['name']
json.image_url User::Avatar.get_letter_avatar_url(commit['commit']['author']['name'])
end
end
end
end

View File

@ -0,0 +1,17 @@
total_count = @contributors.size
json.contributors @contributors.each do |contributor|
user = User.find_by(gitea_uid: contributor["id"])
if contributor["login"] == "root"
total_count -= 1
next
end
json.contributions contributor["contributions"]
json.gid contributor["id"]
json.login user.login
json.type user&.type
json.name user.real_name
json.image_url url_to_avatar(user)
end
json.total_count total_count

View File

@ -0,0 +1,98 @@
json.content @project.content
json.website @project.website
json.lesson_url @project.lesson_url
if @result[:readme].blank?
json.readme nil
else
json.readme @result[:readme].merge(content: readme_render_decode64_content(@result[:readme]["content"], nil))
end
json.identifier render_identifier(@project)
json.name @project.name
json.description @project.description
json.project_id @project.id
json.repo_id @repository.id
json.issues_count @project.issues_count.to_i - @project.pull_requests_count.to_i
json.pull_requests_count @project.pull_requests_count
json.project_identifier render_identifier(@project)
json.praises_count @project.praises_count.to_i
json.forked_count @project.forked_count.to_i
json.watchers_count @project.watchers_count.to_i
json.versions_count @project.versions_count #里程碑数量
json.version_releases_count @project.releases_size(@user.try(:id), "all")
json.version_releasesed_count @project.releases_size(@user.try(:id), "released") #已发行的版本
json.permission render_permission(@user, @project)
json.mirror_url @project&.repository.mirror_url
json.mirror @project&.repository.mirror_url.present?
json.type @project.numerical_for_project_type
json.open_devops @project.open_devops?
unless @project.common?
json.mirror_status @repository.mirror_status
json.mirror_num @repository.mirror_num
json.first_sync @repository.first_sync?
end
json.watched @project.watched_by? @user
json.praised @project.praised_by? @user
json.status @project.status
json.forked_from_project_id @project_fork_id
json.fork_info do
if @fork_project.present?
json.fork_form_name @fork_project.try(:name)
json.fork_project_user_login @fork_project_user.try(:login)
json.fork_project_identifier @fork_project.identifier
json.fork_project_user_name @fork_project_user.try(:show_real_name)
end
end
if @result[:repo]
json.size replace_bytes_to_b(number_to_human_size(@result[:repo]['size'].to_i*1024))
json.ssh_url @result[:repo]['ssh_url']
json.clone_url @result[:repo]['clone_url']
json.default_branch @result[:repo]['default_branch']
json.empty @result[:repo]['empty']
json.full_name @result[:repo]['full_name']
json.private @result[:repo]['private']
end
json.license_name @project.license_name
json.release_versions do
json.list @result[:release].each do |release|
forge_version = VersionRelease.find_by(version_gid: release["id"])
json.id forge_version&.id
json.name release["name"]
json.tag_name release["tag_name"]
json.created_at format_time(release["created_at"].to_time)
end
json.total_count @repository&.version_releases.size
end
json.branches do
json.list @result[:branch].each do |branch|
json.name branch["name"]
end
json.total_count @result[:branch].size
end
json.tags do
json.list @result[:tag].each do |tag|
json.name tag["name"]
end
json.total_count @result[:tag].size
end
json.contributors do
total_count = @result[:contributor].size
json.list @result[:contributor].each do |contributor|
user = User.find_by(gitea_uid: contributor["id"])
if contributor["login"] == "root"
total_count -= 1
next
end
json.contributions contributor["contributions"]
json.gid contributor["id"]
json.login user.login
json.type user&.type
json.name user.real_name
json.image_url url_to_avatar(user)
end
json.total_count total_count
end
json.languages @result[:language]
json.partial! 'author', locals: { user: @project.owner }

View File

@ -5,3 +5,6 @@ json.project_description @project.description
json.project_category_id @project.project_category_id
json.project_language_id @project.project_language_id
json.private !@project.is_public
json.website @project.website
json.project_units @project.project_units.pluck(:unit_type)
json.lesson_url @project.lesson_url

View File

@ -41,7 +41,7 @@ if @project.forge?
end
#json.tags_count @tags_count
#json.branches_count @branches_count
#json.commits_count @commits_count
json.commits_count @commits_count
json.zip_url render_zip_url(@project, @ref)
json.tar_url render_tar_url(@project, @ref)
json.entries do

View File

@ -45,7 +45,15 @@ json.setting do
json.main_site current_laboratory.main_site?
json.new_course default_course_links
json.old_projects_url @old_projects_url
json.add do
json.array! @add
end
json.personal do
json.array! @personal
end
json.common @common
end

View File

@ -1,3 +0,0 @@
json.count @count
json.courses @courses, partial: 'users/courses/shared/course', as: :course

View File

@ -1,18 +0,0 @@
json.id course.id
json.name course.name
# json.members_count course.members_count
json.members_count course.course_members_count
# json.homework_commons_count course.homework_commons_count
json.homework_commons_count get_tasks_count course
json.attachments_count course.attachments.count
json.visits course.visits
json.school course.school&.name
json.first_category_url module_url(course.course_modules.where(hidden: 0).order(position: :desc).first, course)
json.is_public course.is_public
json.can_visited observed_logged_user? || course.can_visited?
json.teacher do
json.partial! 'users/shared/real_user', user: course.teacher
end

View File

@ -0,0 +1,20 @@
json.id @user.id
json.login @user.login
json.name @user.full_name
json.location @user.location
json.image_url url_to_avatar(@user)
json.url "#{request.base_url }/users/#{@user.login}"
json.followers_count @user.followers_count
json.followers_url "#{base_url}/users/#{@user.login}/fan_users"
json.following_count @user.following_count
json.following_url "#{base_url}/users/#{@user.login}/watchers"
json.projects_count @user.projects_count
json.projects_url "#{base_url}/users/#{@user.login}"
json.projects_count @user.projects_count
json.is_watch current_user&.watched?(@user)
json.organizations @user.organizations do |organization|
json.login organization.login
json.name organization.real_name
json.image_url url_to_avatar(organization)
json.url "#{base_url}/organize/#{organization.login}"
end

View File

@ -1,3 +0,0 @@
json.count @count
json.shixuns @shixuns, partial: 'users/shixuns/shared/shixun', as: :shixun, locals: { user: observed_user }

View File

@ -1,10 +0,0 @@
json.id shixun.id
json.identifier shixun.identifier
json.tag shixun.first_tag_repertoire&.name
json.image_url url_to_avatar(shixun)
json.name shixun.name
json.status shixun.status
json.human_status shixun.human_status
json.challenges_count shixun.challenges_count
json.finished_challenges_count @finished_challenges_count_map&.fetch(shixun.id, 0) || shixun.finished_challenges_count(user)
json.is_jupyter shixun.is_jupyter

View File

@ -1,6 +0,0 @@
json.extract! video, :id, :title, :cover_url, :file_url, :play_url, :vv, :user_id
json.play_duration video.video_play_duration
json.published_at video.display_published_at
json.created_at video.display_created_at
json.updated_at video.display_updated_at

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